2014-12-15 2 views
14

Допустим, мне нужен только один метод для запуска в 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, который может быть вызван от метода не async, например? Есть причина, по которой мне не нужна эта конкретная задача: async, но функция Load().

public void GetSomethingElse(){ 
    var task1 = Load().Wait();  
} 

Как это возможно?


Я попытался даже изменить метод Load() использовать var data = task1.Wait() и т.д. вместо await, до сих пор нет разницы, независимо от того, каким образом я стараюсь. Если кто-то может помочь, это будет оценено по достоинству.

+0

try: 'var data1 = ждать Task.Run (() => GetAsync (1));' –

+0

http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638. aspx –

+0

@JohnWoo Я попробовал 'var data1 = task1.Wait();' сделал бы это по-другому? –

ответ

11

У вас, вероятно, есть тупик на руках. Вы блокируете поток, используя Wait(), для задачи, которая нуждается в этом потоке, потому что в ASP.Net используется также SynchronizationContext (также в среде графического интерфейса).

Вы должны использовать 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. 
} 

в вашем случае, когда вы хотите продолжить после все задачи завершена, вы должны использовать Task.WhenAll вместо await в г каждая задача отдельно:

public async Task Load() 
{ 
    await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false); 
    // process data. 
} 

Примечание: делать синхронизации через асинхронном обычно не рекомендуется, поскольку это не имеет никаких преимуществ (вы блокируете поток на протяжении всей операции) и может привести к тупиков (как это один).

+0

Что делать, если 'GetAsync' возвращает переменную? И вы говорите, что я все равно столкнусь с тупиками, используя этот метод? –

+0

@ControlFreak No. ConfigureAwait решает это. Task.WhenAll просто удобнее в этом случае. – i3arnon

+0

@ControlFreak Я обновил ответ, чтобы сделать это более понятным. – i3arnon

1

Вы бы изменить функцию нагрузки следующим образом:

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.. 
    }); 
} 
2

Я думаю, что у вас есть классический сценарий тупиковый. См. this post для получения более подробной информации. Когда у вас есть оператор await, текущий SynchronizationContext сохраняется до вызова await и восстанавливается после этого, а остальная часть метода отправляется на него.В приложении GUI существует только один поток, связанный с этим контекстом, поэтому остальная часть метода будет выполняться в потоке GUI, но не может , потому что Wait() является блокирующим вызовом, который блокирует поток графического интерфейса.

Попробуйте вместо этого:

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.. 
} 

Если есть какие-либо Awaits внутри GetAsync вы, возможно, придется добавить .ConfigureAwait(false) там.

Смежные вопросы