Deadlock при использовании ExecuteReaderAsync() [дубликат]

484
01 июня 2017, 06:27

На данный вопрос уже ответили:

  • Зависает оператор `await` в оконном приложении / программа висит при вызове Task.Result или Wait 1 ответ

Похоже что получаю Deadlock при использовании ExecuteReaderAsync(). Не могу понять что не так

Task task = ExecReader(tempList, query);
task.Wait();
async Task ExecReader(List<string> tempList, string query)
{            
     using (SqlConnection sqlConn = new SqlConnection(connStr))
     {
        try
          {                    
             sqlConn.Open();
             SqlCommand sqlCmd = new SqlCommand(query, sqlConn);
             SqlDataReader reader = await sqlCmd.ExecuteReaderAsync();
             while(reader.Read())
             {
                tempList.Add(reader.GetString(0));                               
             }
             catch(Exception e)
             { }                
          }            
     }
}

Доходит до ExecuteReaderAsync() и все, виснит наглухо. Пробовал через GetAwaiter(), но в таком случае главный поток не дожидается выполнения задачи, и берет управление сразу после ExecuteReaderAsync(). А если через GetAwaiter().GetResult(), то снова висит.

Answer 1

Вы сделали две три ошибки, которые вместе приводят к данной проблеме.

Первая из них — вы пользуетесь синхронным Wait. Этот Wait блокирует поток до окончания задачи, а значит, поток будет висеть. Если внутри функции await захочет вернуться в данный поток (судя по всему, вы запустили Task в UI-потоке), он не сможет этого сделать потому, что поток заблокирован — deadlock.

Решение — не пользуйтесь task.Wait();, пользуйтесь await task;.

Вторая ошибка — вы в коде, который не рассчитывает на возвращение в исходный поток, не используете .ConfigureAwait(false). Отсутствие .ConfigureAwait(false) заставляет код хотеть вернуться в UI-поток, если он был в нём запущен.

Пишите

SqlDataReader reader = await sqlCmd.ExecuteReaderAsync().ConfigureAwait(false);

Третья ошибка — вы запускаете Task, работающий с базой данных и содержащий синхронные команды (sqlConn.Open();, например), в непонятно каком, может быть и в UI-потоке. Так делать нельзя, вынесите это всё в фоновый поток.

Итого:

Task task = ExecReader(tempList, query);
await task;
public Task ExecReader(List<string> tempList, string query)
{
    async Task Impl()
    {
        using (SqlConnection sqlConn = new SqlConnection(connStr))
        {
            try
            {
                sqlConn.Open();
                SqlCommand sqlCmd = new SqlCommand(query, sqlConn);
                SqlDataReader reader = await sqlCmd.ExecuteReaderAsync()
                                                   .ConfigureAwait(false);
                while (reader.Read())
                {
                    tempList.Add(reader.GetString(0));
                }
            }
            catch (Exception e)
            { }
        }
    }
    return Task.Run(Impl);
}

Ещё по теме: Stephen Cleary Don't Block on Async Code.

READ ALSO
Закрытый DataReader

Закрытый DataReader

Нужно получить несколько таблиц из Mysql БД и записать их в DataSetВернее это даже не таблицы а выборки из двух таблиц слитые в таблицу через UNION

545
Как сделать авторазметку grid?

Как сделать авторазметку grid?

При заполнении колонки кнопками до низа, появляется прокрутка внизКак вместо прокрутки сделать добавление второго столбца и продолжить...

387
Проигрывание файлов с помощью winmm.dll

Проигрывание файлов с помощью winmm.dll

Собственно, задача следующая, используя winmmdll воспроизвести файл

324
CefSharp программно нажать на кнопку

CefSharp программно нажать на кнопку

Доброго времени суток, не подскажите, как программно нажать на кнопку использую cefsharp (chromium), код кнопки

745