2016-06-07 1 views
3

Я бы сказал, что следующие два фрагмента кода, которые у меня есть, эквивалентны, но это не так.Метод расширения не эквивалентен прямому вызову

Ниже работает правильно:

var entry3 = Task.Run(async() => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault(); 

Следующий код, где я только переехал Task.Run.WaitForResult цепь в метод расширения, не работает, но производит затор:

var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault(); 

public static T RunSynchronouslyAndReturnResult<T>(this Task<T> task) 
{ 
    return Task.Run(async() => await task).WaitForResult(); 
} 

Почему эти два фрагмента кода не эквивалентны?

Для полноты использования метод GetMemberGroupsAsync предоставляется API-интерфейсом Microsoft Azure Graph, а функция WaitForResult определена ниже. Насколько я вижу, он не делает ничего другого в зависимости от имени вызывающего абонента или sth. как то:

public static TResult WaitForResult<TResult>(this Task<TResult> task, 
              bool continueOnCapturedContext = false) 
{ 
    if (task == null) 
    { 
     throw new ArgumentNullException("task"); 
    } 

    try 
    { 
     return PreventForDeadLocks(task, continueOnCapturedContext).Result; 
    } 
    catch (AggregateException ex) 
    { 
     if (ex.InnerExceptions.Count == 1) 
     { 
      throw ex.InnerExceptions[0]; 
     } 

     throw; 
    } 
} 

public static async Task<TResult> PreventForDeadLocks<TResult>(this Task<TResult> task, 
                   bool continueOnCapturedContext = false) 
{ 
    return await task.ConfigureAwait(continueOnCapturedContext: continueOnCapturedContext); 
} 
+1

Хуже того, 'PreventForDeadlocks' не предотвратит взаимоблокировки. ** Нет шаблона sync-over-async, который работает во всех ситуациях! ** –

+0

Почему бы просто не сделать var var3 = (ждать entry2.GetMemberGroupsAsync (false)). FirstOrDefault(); '? Это потому, что это потребует от вас сделать метод, чтобы этот код находился внутри 'async'? Как правило, вы позволяете шаблону 'async'-'await' выходить на ваши события, а затем создавать эти« async void »(огонь и забыть). – juharr

+0

@juharr только, что у меня нет событий - код находится внутри конечной точки WebAPI. Могу ли я сделать асинхронную конечную точку WebAPI? – Alexander

ответ

3

Разница заключается в том, в каком контексте синхронизации запускается ваша задача. Здесь:

var entry3 = Task.Run(async() => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault(); 

запуск асинхронной задача (я имею в виду await entry2.GetMemberGroupsAsync(false)) внутри Task.Run вызова, поэтому контекст синхронизации UI не улавливается. Но здесь:

var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault(); 

Вы неявно запустить задачу (entry2.GetMemberGroupsAsync(false) возвращает Task) на контексте пользовательского интерфейса, поэтому контекст синхронизации UI захватывается, и у вас есть тупик.

3

В первом случае GetMemberGroupsAsync вызывается в другом потоке, чем WaitForResult.

Во втором случае он вызывается в той же ветке, что и WaitForResult. Вы просто в ожидании в другой теме.

+0

Почему это; это метод расширения, выполняемый в другом потоке? – Alexander

+0

В первом случае GetMemberGroupsAsync вызывается внутри Task.Run, поэтому он запускается в другом потоке. Во втором случае сначала вызывается GetMemberGroupsAsync (так что задача уже запущена), а затем результирующая задача передается в Task.Run. Таким образом, во втором случае GetMemberGroupsAsync _not_ вызывается в другом потоке. Имеет смысл? –

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