2016-03-14 4 views
2

Я создаю (хорошо адаптирую существующий) вспомогательный класс, чтобы позволить мне запускать методы async синхронно. До сих пор у меня есть фабрика задач и статический метод, который позволяет мне запускать методы async синхронно, пока я не настраиваю их с помощью .ConfigureAwait, потому что .ConfigureAwait возвращает возвращаемое значение метода из System.Threading.Tasks.Task<T> в System.Runtime.CompilerServices.ConfiguredTaskAwaitable<T>. Код выглядит следующим образом:Что эквивалентно Unwrap для ConfiguredTaskAwaitable?

public static class AsyncUtils { 
    private static readonly TaskFactory _taskFactory = 
     new TaskFactory(
      CancellationToken.None, 
      TaskCreationOptions.None, 
      TaskContinuationOptions.None, 
      TaskScheduler.Default 
     ); 

    public static TResult RunSync<TResult>(Func<Task<TResult>> func) { 
     return _taskFactory 
      .StartNew<Task<TResult>>(func) 
      .Unwrap<TResult>() 
      .GetAwaiter() 
      .GetResult(); 
    } 
} 

Если я пытаюсь добавить метод для решения ConfiguredTaskAwaitable<T>, это может выглядеть следующим образом:

public static TResult RunSync<TResult>(Func<ConfiguredTaskAwaitable<TResult>> func) { 
     return _taskFactory 
      .StartNew<ConfiguredTaskAwaitable<TResult>>(func) 
      .Unwrap<TResult>() // Doesn't exist! 
      .GetAwaiter() 
      .GetResult(); 
    } 

Беда в том, как можно видеть из комментария, StartNew<ConfiguredTaskAwaitable> возвращает Task<ConfiguredTaskAwaitable<TResult>> вместо Task<Task<TResult>>, что означает, что для него не существует метода Unwrap. Есть ли эквивалент? Как создать эквивалентный метод для ConfiguredTaskAwaitable?

+2

Почему вы делая это? –

+0

@Jez: Я второй вопрос Юваля: почему? Я также могу сказать по опыту, что ** невозможно ** создать общую оболочку sync-over-async, которая корректно работает во всех сценариях. –

+0

Это приложение, которое нужно отключить и вызвать веб-службу при запуске (вызов веб-службы isync), и может продолжаться только после получения ответа. Возможно, это не идеальное приложение, но я должен его поддерживать. :-) – Jez

ответ

2

ConfigureAwait() только контролирует, как контекст возобновляется после await. Если вы получаете результат синхронно, это не будет иметь никакого эффекта, потому что нет «резюме».

Кроме того, один, как правило, запустить асинхронные методы синхронно, просто делая это:

TResult RunSync<TResult>(Func<Task<TResult>> func) { 
    return func().Result; 
} 

Или это, если вы хотите, чтобы заставить его работать на пуле потоков:

TResult RunSync<TResult>(Func<Task<TResult>> func) { 
    return Task.Run(func).Result; 
} 
+0

Ну почему-то Microsoft сделала это по-другому. :-) Что-то делать с разворачивающимися исключениями, которые происходят в асинхронном методе? – Jez

+2

Разница семантична. 'GetAwaiter.GetResult' даст внутреннее разворачиваемое исключение, в то время как' Task.Result' даст 'AggregateException'. Оба могут с большой вероятностью вызвать взаимоблокировки (исключая, если для параметра «ConfiguredTaskAwaitable» установлено значение false) –

+0

@YuvalItzchakov. Так что я мог бы сделать с помощью метода «RunSync», который принимает «ConfiguredTaskAwaitable», поэтому я могу установить его на false? – Jez

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