У меня есть public async void Foo()
метод, который я хочу вызвать из синхронного метода. До сих пор все, что я видел из документации MSDN, это вызов асинхронных методов через асинхронные методы, но вся моя программа не построена с асинхронными методами.
Это вообще возможно?
Вот один пример вызова этих методов из асинхронного метода: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx
Теперь я смотрю на вызов этих асинхронных методов из методов синхронизации.
c#
async-await
башня
источник
источник
async void Foo()
метод не возвращает a,Task
это означает, что вызывающая сторона не может знать, когда он завершится,Task
вместо этого он должен вернуть .Ответы:
Асинхронное программирование «растет» через базу кода. Это было по сравнению с вирусом зомби . Лучшее решение - позволить ему расти, но иногда это невозможно.
Я написал несколько типов в моей библиотеке Nito.AsyncEx для работы с частично асинхронной базой кода. Там нет решения, которое работает в любой ситуации, хотя.
Решение А
Если у вас есть простой асинхронный метод, который не нуждается в синхронизации обратно в его контекст, то вы можете использовать
Task.WaitAndUnwrapException
:Вы не хотите использовать
Task.Wait
илиTask.Result
потому что они обертывают исключения вAggregateException
.Это решение подходит только в том случае, если
MyAsyncMethod
не выполняется обратная синхронизация с его контекстом. Другими словами, каждыйawait
вMyAsyncMethod
должен заканчиватьсяConfigureAwait(false)
. Это означает, что он не может обновлять какие-либо элементы пользовательского интерфейса или обращаться к контексту запроса ASP.NET.Решение Б
Если
MyAsyncMethod
необходимо синхронизировать обратно с его контекстом, то вы можете использовать егоAsyncContext.RunTask
для предоставления вложенного контекста:* Обновление от 14.04.2014: В более поздних версиях библиотеки API выглядит следующим образом:
(Можно использовать
Task.Result
в этом примере, потому чтоRunTask
будут распространятьсяTask
исключения).Причина, по которой вам может понадобиться
AsyncContext.RunTask
вместо этого,Task.WaitAndUnwrapException
заключается в том, что в WinForms / WPF / SL / ASP.NET возможна довольно слабая тупиковая ситуация:Task
.Task
.async
Метод используетawait
безConfigureAwait
.Task
Не может завершить в этой ситуации , потому что завершается только тогда , когдаasync
метод законченный;async
метод не может завершить , потому что он пытается планировать свое продолжение кSynchronizationContext
и WinForms / WPF / SL / ASP.NET не позволит запустить продолжение , так как синхронный метод уже работает в этом контексте.Это одна из причин, почему стоит использовать как можно больше
ConfigureAwait(false)
внутри каждогоasync
метода.Решение С
AsyncContext.RunTask
не будет работать в каждом сценарии. Например, еслиasync
метод ожидает чего-то, что требует события пользовательского интерфейса для завершения, то вы зашли в тупик даже с вложенным контекстом. В этом случае вы можете запуститьasync
метод в пуле потоков:Однако это решение требует,
MyAsyncMethod
чтобы он работал в контексте пула потоков. Поэтому он не может обновлять элементы пользовательского интерфейса или обращаться к контексту запроса ASP.NET. И в этом случае вы можете также добавитьConfigureAwait(false)
к егоawait
заявлениям и использовать решение А.Обновление, 2019-05-01: Текущие «наихудшие практики» описаны в статье MSDN здесь .
источник
WaitAndUnwrapException
мой собственный метод из моей библиотеки AsyncEx . Официальные библиотеки .NET не предоставляют большой помощи для смешивания синхронизации и асинхронного кода (и вообще, вы не должны этого делать!). Я жду .NET 4.5 RTW и новый ноутбук без XP, прежде чем обновлять AsyncEx для работы на 4.5 (в настоящее время я не могу разрабатывать для 4.5, потому что я застрял на XP еще несколько недель).AsyncContext
теперь естьRun
метод, который принимает лямбда-выражения, поэтому вы должны использоватьvar result = AsyncContext.Run(() => MyAsyncMethod());
RunTask
метода. Самая близкая вещь, которую я мог найти, былаRun
, но у этого нетResult
собственности.var result = AsyncContext.Run(MyAsyncMethod);
Добавление решения, которое окончательно решило мою проблему, надеюсь, сэкономит чье-то время.
Сначала прочитайте пару статей Стивена Клири :
Из «двух лучших практик» в «Не блокировать в асинхронном коде», первая не работала для меня, а вторая неприменима (в основном, если я могу использовать
await
, я делаю!).Итак, вот мой обходной путь: оберните вызов внутри
Task.Run<>(async () => await FunctionAsync());
и, надеюсь, больше не тупик .Вот мой код:
источник
Task.Run()
это не лучшая практика в асинхронном коде. Но, опять же, каков ответ на оригинальный вопрос? Никогда не вызывать асинхронный метод синхронно? Мы желаем, но в реальном мире иногда приходится.Parallel.ForEach
злоупотребления не будут иметь эффекта в «реальном мире», и в конечном итоге это сломало серверы. Этот код подходит для консольных приложений, но, как говорит @ChrisPratt, его не следует использовать в веб-приложениях. Это может работать "сейчас", но не масштабируется.Microsoft создала AsyncHelper (внутренний) класс для запуска Async как Sync. Источник выглядит так:
Базовые классы Microsoft.AspNet.Identity имеют только методы Async, и для вызова их как Sync существуют классы с методами расширения, которые выглядят следующим образом (пример использования):
Для тех, кто обеспокоен условиями лицензирования кода, здесь есть ссылка на очень похожий код (просто добавлена поддержка культуры в потоке), в которой есть комментарии, указывающие, что это лицензия MIT от Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs
источник
await
звонкиConfigureAwait(false)
. Я попытался использоватьAsyncHelper.RunSync
для вызова асинхронной функции изApplication_Start()
функции в Global.asax, и это похоже на работу. Означает ли это, чтоAsyncHelper.RunSync
надежно не подвержена проблеме взаимоблокировки «маршала обратно в контекст звонящего», о которой я читал в других разделах этой публикации?async Main теперь является частью C # 7.2 и может быть включен в расширенных настройках проекта.
Для C # <7.2 правильный путь:
Вы увидите, что это используется во многих документах Microsoft, например: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- Тем-подписка
источник
MainAsync().Wait()
?Вы читаете ключевое слово «await» как «запустите эту долгосрочную задачу, а затем верните управление вызывающему методу». Как только долгосрочное задание выполнено, оно выполняет код после него. Код после await похож на то, что раньше было методами CallBack. Большая разница в том, что логический поток не прерывается, что делает его намного легче писать и читать.
источник
Wait
Оборачивает исключения и имеет возможность тупика.await
, он будет выполняться синхронно. По крайней мере, это работает для меня (без звонкаmyTask.Wait
). На самом деле, я получил исключение, когда попытался позвонить,myTask.RunSynchronously()
потому что он уже был выполнен!.Result
.Result
в тот момент вызов блокировался , поэтому он никогда не попадал туда. ИResult
никогда не кончается, потому что он ждет того, кто ждетResult
конца, в основном: DЯ не уверен на 100%, но я считаю, что методика, описанная в этом блоге, должна работать во многих случаях:
источник
Однако есть хорошее решение, которое работает (почти: см. Комментарии) в любой ситуации: специальный механизм обработки сообщений (SynchronizationContext).
Вызывающий поток будет заблокирован, как и ожидалось, при этом гарантируя, что все продолжения, вызываемые из асинхронной функции, не будут взаимоблокированы, поскольку они будут перенаправлены в специальный SynchronizationContext (насос сообщений), работающий в вызывающем потоке.
Код специального помощника по отправке сообщений:
Применение:
Более подробное описание асинхронного насоса доступно здесь .
источник
Всем, кто больше обращает внимание на этот вопрос ...
Если вы посмотрите,
Microsoft.VisualStudio.Services.WebApi
есть класс с именемTaskExtensions
. В этом классе вы увидите статический метод расширенияTask.SyncResult()
, который, как и полностью, просто блокирует поток до тех пор, пока задача не вернется.Внутренне он вызывает
task.GetAwaiter().GetResult()
довольно просто, но перегружен для работы с любымasync
возвращаемым методомTask
,Task<T>
илиTask<HttpResponseMessage>
... синтаксический сахар, ребенок ... папа получил сладкий зуб.Похоже,
...GetAwaiter().GetResult()
это официальный MS-способ выполнения асинхронного кода в контексте блокировки. Кажется, работает очень хорошо для моего случая использования.источник
Или используйте это:
источник
Вы можете вызывать любой асинхронный метод из синхронного кода, то есть до тех пор, пока он вам не понадобится
await
, и в этом случае они также должны быть отмеченыasync
.Как многие здесь предлагают, вы можете вызвать Wait () или Result для результирующей задачи в вашем синхронном методе, но затем вы получите блокирующий вызов в этом методе, что побеждает асинхронную цель.
Если вы действительно не можете создать свой метод
async
и не хотите блокировать синхронный метод, тогда вам придется использовать метод обратного вызова, передав его в качестве параметра методу ContinueWith в задаче.источник
async
» отвлекли мое внимание от того, что вы на самом деле говорили.Я знаю, что так поздно. Но в случае, если кто-то, как я, хотел бы решить это аккуратно, легко и без зависимости от другой библиотеки.
Я нашел следующий кусок кода от Райана
тогда вы можете назвать это так
источник
После нескольких часов проб разных методов, с более или менее успешным завершением, на этом я и закончил. Он не заканчивается тупиком при получении результата, а также получает и выдает исходное исключение, а не завернутое.
источник
Он может быть вызван из нового потока (НЕ из пула потоков!):
источник
Эти асинхронные методы Windows имеют изящный маленький метод, называемый AsTask (). Вы можете использовать это, чтобы метод возвращал себя в качестве задачи, чтобы вы могли вручную вызывать Wait () для него.
Например, в приложении Windows Phone 8 Silverlight вы можете сделать следующее:
Надеюсь это поможет!
источник
Если вы хотите запустить его Sync
источник
RunSynchronously()
горячей задачи приводит кInvalidOperationException
. Попробуйте это с этим кодом:Task.Run(() => {}).RunSynchronously();
источник