У меня есть async
метод:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
Мне нужно вызвать этот метод из синхронного метода.
Как я могу сделать это, не дублируя GenerateCodeAsync
метод, чтобы он работал синхронно?
Обновить
Пока не найдено разумного решения.
Тем не менее, я вижу, что HttpClient
уже реализует этот шаблон
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Ответы:
Вы можете получить доступ к
Result
свойству задачи, что приведет к блокировке вашего потока, пока не станет доступен результат:Примечание. В некоторых случаях это может привести к тупику: ваш вызов
Result
блокирует основной поток, предотвращая тем самым выполнение остальной части асинхронного кода. У вас есть следующие варианты, чтобы убедиться, что этого не произойдет:.ConfigureAwait(false)
в свою библиотеку метод илиявно выполните ваш асинхронный метод в потоке пула потоков и дождитесь его завершения:
Это не значит, что вы должны просто бездумно добавлять
.ConfigureAwait(false)
после всех ваших асинхронных вызовов! Для подробного анализа того, почему и когда вы должны использовать.ConfigureAwait(false)
, см. Следующий пост в блоге:источник
result
риски в тупике, а затем , когда это безопасно , чтобы получить результат? Каждый асинхронный вызов требуетTask.Run
илиConfigureAwait(false)
?AspNetSynchronizationContext.Post
сериализует асинхронные продолжения:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
чтобы оставаться в безопасности. Или используйте что-то вродеWithNoContext
сокращения избыточных потоков..Result
может оставаться в тупике, если вызывающий абонент находится в самом пуле потоков. Возьмем сценарий, в котором пул потоков имеет размер 32, и 32 задачи выполняются иWait()/Result
ожидают запланированной 33-й задачи, которая должна быть запущена в одном из ожидающих потоков.Вы должны получить awaiter (
GetAwaiter()
) и завершить ожидание завершения асинхронной задачи (GetResult()
).источник
Task.GetAwaiter
: этот метод предназначен для использования компилятором, а не для использования в коде приложения.Вы должны быть в состоянии сделать это с помощью делегатов, лямбда-выражения
источник
Это возможно с
GenerateCodeAsync().Result
илиGenerateCodeAsync().Wait()
, как предполагает другой ответ. Это заблокирует текущий поток, покаGenerateCodeAsync
не завершится.Тем не менее, ваш вопрос помечен как asp.netи вы также оставили комментарий:
Моя точка зрения, вы не должны блокировать асинхронный метод в ASP.NET. Это уменьшит масштабируемость вашего веб-приложения и может создать тупик (когда
await
продолжениеGenerateCodeAsync
размещено внутриAspNetSynchronizationContext
). ИспользованиеTask.Run(...).Result
для разгрузки чего-либо в поток пула, а затем его блокирование еще больше повредит масштабируемости, поскольку для обработки данного HTTP-запроса потребуется еще +1 поток.ASP.NET имеет встроенную поддержку асинхронных методов, либо с помощью асинхронных контроллеров (в ASP.NET MVC и Web API) или непосредственно с помощью
AsyncManager
иPageAsyncTask
в классическом ASP.NET. Вы должны использовать это. Для более подробной информации, проверьте этот ответ .источник
SaveChanges()
методDbContext
, и здесь я вызываю асинхронные методы, поэтому, к сожалению, асинхронный контроллер не поможет мне в этой ситуацииSaveChangesAsync
иSaveChanges
просто убедиться, что они не вызываются в одном и том же проекте ASP.NET..NET MVC
НапримерIAuthorizationFilter
, не все фильтры поддерживают асинхронный код, поэтому я не могу использовать егоasync
полностьюУ Microsoft Identity есть методы расширения, которые синхронно вызывают асинхронные методы. Например, есть метод GenerateUserIdentityAsync () и равный CreateIdentity ()
Если вы посмотрите на UserManagerExtensions.CreateIdentity (), это будет выглядеть так:
Теперь давайте посмотрим, что делает AsyncHelper.RunSync
Итак, это ваша оболочка для асинхронного метода. И, пожалуйста, не читайте данные из Result - это потенциально заблокирует ваш код в ASP.
Есть и другой способ - который мне кажется подозрительным, но вы тоже можете это рассмотреть
источник
Чтобы предотвратить взаимные блокировки, я всегда стараюсь использовать,
Task.Run()
когда мне нужно синхронно вызывать асинхронный метод, который упоминает @Heinzi.Однако метод должен быть модифицирован, если асинхронный метод использует параметры. Например
Task.Run(GenerateCodeAsync("test")).Result
выдает ошибку:Это можно назвать так:
источник
Большинство ответов в этой теме либо сложны, либо приводят к тупику.
Следующий метод прост и позволит избежать тупика, потому что мы ждем завершения задачи и только потом получаем ее результат.
Кроме того, здесь есть ссылка на статью MSDN, в которой говорится об одном и том же - https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- в-основного контекста /
источник
Я предпочитаю неблокирующий подход:
источник
Ну, я использую этот подход:
источник
Другой способ может быть, если вы хотите дождаться завершения задачи:
источник
РЕДАКТИРОВАТЬ:
Задача имеет метод Wait, Task.Wait (), который ожидает разрешения «обещания», а затем продолжает работу, что делает его синхронным. пример:
источник
Если у вас есть асинхронный метод RefreshList , вы можете вызвать этот асинхронный метод из неасинхронного метода, как показано ниже.
источник