Использование async в неасинхронном методе
Предположим, что мне нужен только один метод для запуска в async
.
Итак, у меня есть метод async
, как показано ниже:
public async Task Load(){
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
И затем я пытаюсь вызвать этот метод async
в другом методе как задачу, и хотел бы, чтобы он подождал, пока этот конкретный фрагмент кода async
не будет выполнен. Проблема в том, что нет. Когда он достигает первого await
в Load()
, он просто не заканчивает загрузку. Отладчик пуст и не дает никакой другой ошибки.
Можно ли вызывать метод async
из метода non async
, как это?
Есть причина, по которой мне не нужна эта конкретная задача async
, но функция Load()
, которую я делаю.
public void GetSomethingElse(){
var task1 = Load().Wait();
}
Как это возможно?
Я попытался даже изменить метод Load()
на использование var data = task1.Wait()
и т.д. вместо await
, но все равно никакой разницы, независимо от того, каким способом я пытаюсь. Если кто-то может помочь, это будет оценено.
Ответы
Ответ 1
У вас наверняка есть тупик на ваших руках. Вы блокируете поток с помощью Wait()
для задачи, которая нуждается в этом потоке, потому что там используется SynchronizationContext
в ASP.Net(также в среде графического интерфейса).
Вы должны использовать ConfigureAwait(false)
, чтобы сообщить awaiter не фиксировать этот контекст. Этого достаточно, чтобы сделать это на первом await
, так как в остальном не было бы SynchronizationContext
для захвата:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2;
var data3 = await task3;
//..process data.
}
Тем не менее, рекомендуется всегда использовать ConfigureAwait
, если вы хотите захватить SynchronizationContext
, поэтому лучшим стандартом является следующее:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2.ConfigureAwait(false);
var data3 = await task3.ConfigureAwait(false);
//..process data.
}
В вашем случае, когда вы хотите продолжить выполнение all завершенных задач, вы должны использовать Task.WhenAll
вместо await
для каждой задачи отдельно:
public async Task Load()
{
await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false);
// process data.
}
Примечание. Выполнение синхронизации по async обычно обескураживается, поскольку оно не имеет преимуществ (вы блокируете поток на протяжении всей операции) и может вызывать взаимоблокировки (например, этот).
Ответ 2
Я думаю, что у вас классический сценарий тупиковой ситуации. Подробнее см. этот пост. Когда у вас есть оператор await
, текущий SynchronizationContext
сохраняется перед вызовом await
и восстанавливается впоследствии, а остальная часть метода отправляется на него. В приложении с графическим интерфейсом есть только один поток, связанный с этим контекстом, поэтому остальная часть метода будет выполняться в потоке GUI, но не может
потому что Wait()
- это блокирующий вызов, который блокирует поток GUI.
Попробуйте это вместо:
public async Task Load(){
Task task1 = GetAsync(1).ConfigureAwait(false);
Task task2 = GetAsync(2).ConfigureAwait(false);
Task task3 = GetAsync(3).ConfigureAwait(false);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
Если в GetAsync
есть какие-либо ожидания, возможно, вам также придется добавить .ConfigureAwait(false)
.
Ответ 3
Вы изменили бы свою функцию загрузки следующим образом:
public async Task Load(){
await new TaskFactory().StartNew(() =>
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
});
}