Как вызвать асинхронный метод из синхронного метода в C #?


865

У меня есть public async void Foo()метод, который я хочу вызвать из синхронного метода. До сих пор все, что я видел из документации MSDN, это вызов асинхронных методов через асинхронные методы, но вся моя программа не построена с асинхронными методами.

Это вообще возможно?

Вот один пример вызова этих методов из асинхронного метода: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

Теперь я смотрю на вызов этих асинхронных методов из методов синхронизации.


2
Я столкнулся с этим также. Переопределяя RoleProvider, вы не можете изменить сигнатуру метода GetRolesForUser, поэтому вы не можете сделать метод асинхронным и поэтому не можете использовать await для вызова api асинхронно. Мое временное решение состояло в том, чтобы добавить синхронные методы в мой общий класс HttpClient, но я хотел бы знать, возможно ли это (и каковы могут быть последствия).
— Тимоти Ли Рассел

1
Поскольку ваш async void Foo()метод не возвращает a, Taskэто означает, что вызывающая сторона не может знать, когда он завершится, Taskвместо этого он должен вернуть .
— Дай

1
Связывание связанных вопросов о том, как это сделать в потоке пользовательского интерфейса.
— нос

Ответы:


714

Асинхронное программирование «растет» через базу кода. Это было по сравнению с вирусом зомби . Лучшее решение - позволить ему расти, но иногда это невозможно.

Я написал несколько типов в моей библиотеке Nito.AsyncEx для работы с частично асинхронной базой кода. Там нет решения, которое работает в любой ситуации, хотя.

Решение А

Если у вас есть простой асинхронный метод, который не нуждается в синхронизации обратно в его контекст, то вы можете использовать Task.WaitAndUnwrapException:

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

Вы не хотите использовать Task.Waitили Task.Resultпотому что они обертывают исключения в AggregateException.

Это решение подходит только в том случае, если MyAsyncMethodне выполняется обратная синхронизация с его контекстом. Другими словами, каждый awaitв MyAsyncMethodдолжен заканчиваться ConfigureAwait(false). Это означает, что он не может обновлять какие-либо элементы пользовательского интерфейса или обращаться к контексту запроса ASP.NET.

Решение Б

Если MyAsyncMethodнеобходимо синхронизировать обратно с его контекстом, то вы можете использовать его AsyncContext.RunTaskдля предоставления вложенного контекста:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* Обновление от 14.04.2014: В более поздних версиях библиотеки API выглядит следующим образом:

var result = AsyncContext.Run(MyAsyncMethod);

(Можно использовать Task.Resultв этом примере, потому что RunTaskбудут распространяться Taskисключения).

Причина, по которой вам может понадобиться AsyncContext.RunTaskвместо этого, Task.WaitAndUnwrapExceptionзаключается в том, что в WinForms / WPF / SL / ASP.NET возможна довольно слабая тупиковая ситуация:

  1. Синхронный метод вызывает асинхронный метод, получая Task.
  2. Синхронный метод блокирует ожидание Task.
  3. asyncМетод использует awaitбез ConfigureAwait.
  4. TaskНе может завершить в этой ситуации , потому что завершается только тогда , когда asyncметод законченный; asyncметод не может завершить , потому что он пытается планировать свое продолжение к SynchronizationContextи WinForms / WPF / SL / ASP.NET не позволит запустить продолжение , так как синхронный метод уже работает в этом контексте.

Это одна из причин, почему стоит использовать как можно больше ConfigureAwait(false)внутри каждого asyncметода.

Решение С

AsyncContext.RunTaskне будет работать в каждом сценарии. Например, если asyncметод ожидает чего-то, что требует события пользовательского интерфейса для завершения, то вы зашли в тупик даже с вложенным контекстом. В этом случае вы можете запустить asyncметод в пуле потоков:

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

Однако это решение требует, MyAsyncMethodчтобы он работал в контексте пула потоков. Поэтому он не может обновлять элементы пользовательского интерфейса или обращаться к контексту запроса ASP.NET. И в этом случае вы можете также добавить ConfigureAwait(false)к его awaitзаявлениям и использовать решение А.

Обновление, 2019-05-01: Текущие «наихудшие практики» описаны в статье MSDN здесь .


9
Решение A похоже на то, что я хочу, но похоже на task.WaitAndUnwrapException () не попало в .Net 4.5 RC; у него есть только задача. Wait (). Есть идеи, как это сделать с новой версией? Или это пользовательский метод расширения, который вы написали?
— deadlydog

3
WaitAndUnwrapExceptionмой собственный метод из моей библиотеки AsyncEx . Официальные библиотеки .NET не предоставляют большой помощи для смешивания синхронизации и асинхронного кода (и вообще, вы не должны этого делать!). Я жду .NET 4.5 RTW и новый ноутбук без XP, прежде чем обновлять AsyncEx для работы на 4.5 (в настоящее время я не могу разрабатывать для 4.5, потому что я застрял на XP еще несколько недель).
— Стивен Клири

12
AsyncContextтеперь есть Runметод, который принимает лямбда-выражения, поэтому вы должны использоватьvar result = AsyncContext.Run(() => MyAsyncMethod());
— Стивен Клири

1
Я получил вашу библиотеку от Nuget, но, похоже, у нее нет RunTaskметода. Самая близкая вещь, которую я мог найти, была Run, но у этого нет Resultсобственности.
— Асад Саидуддин


313

Добавление решения, которое окончательно решило мою проблему, надеюсь, сэкономит чье-то время.

Сначала прочитайте пару статей Стивена Клири :

Из «двух лучших практик» в «Не блокировать в асинхронном коде», первая не работала для меня, а вторая неприменима (в основном, если я могу использовать await, я делаю!).

Итак, вот мой обходной путь: оберните вызов внутри Task.Run<>(async () => await FunctionAsync());и, надеюсь, больше не тупик .

Вот мой код:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

5
Два года спустя мне любопытно узнать, как продвигается это решение. Любые новости? Есть ли тонкость в этом подходе, который теряется на новичках?
— Дэн Эспарза

26
Это не будет взаимоблокировкой, правда, но просто потому, что он вынужден работать в новом потоке, вне контекста синхронизации исходного потока. Тем не менее, есть определенные среды, в которых это очень плохо, особенно веб-приложения. Это может эффективно сократить вдвое доступные потоки для веб-сервера (один поток для запроса и один для этого). Чем больше вы это делаете, тем хуже становится. Вы можете потенциально заблокировать весь ваш веб-сервер.
— Крис Пратт

30
@ChrisPratt - Вы можете быть правы, потому что Task.Run()это не лучшая практика в асинхронном коде. Но, опять же, каков ответ на оригинальный вопрос? Никогда не вызывать асинхронный метод синхронно? Мы желаем, но в реальном мире иногда приходится.
— Tohid

1
@ Ты можешь попробовать библиотеку Стивена Клири. Я видел, как люди предполагают это, и Parallel.ForEachзлоупотребления не будут иметь эффекта в «реальном мире», и в конечном итоге это сломало серверы. Этот код подходит для консольных приложений, но, как говорит @ChrisPratt, его не следует использовать в веб-приложениях. Это может работать "сейчас", но не масштабируется.
— Махдуми

1
Я заинтригован, чтобы начать создавать новые аккаунты в SO, отвечая на вопросы, просто чтобы набрать достаточно очков, чтобы проголосовать за этот ...
— Giannis Paraskevopoulos

206

Microsoft создала AsyncHelper (внутренний) класс для запуска Async как Sync. Источник выглядит так:

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

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

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

Базовые классы Microsoft.AspNet.Identity имеют только методы Async, и для вызова их как Sync существуют классы с методами расширения, которые выглядят следующим образом (пример использования):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

Для тех, кто обеспокоен условиями лицензирования кода, здесь есть ссылка на очень похожий код (просто добавлена ​​поддержка культуры в потоке), в которой есть комментарии, указывающие, что это лицензия MIT от Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
Мои асинхронные методы ждут других асинхронных методов. Я НЕ украшаю свои awaitзвонки ConfigureAwait(false). Я попытался использовать AsyncHelper.RunSyncдля вызова асинхронной функции из Application_Start()функции в Global.asax, и это похоже на работу. Означает ли это, что AsyncHelper.RunSyncнадежно не подвержена проблеме взаимоблокировки «маршала обратно в контекст звонящего», о которой я читал в других разделах этой публикации?
— Bob.at.Indigo.Health

1
@ Bob.at.SBS зависит от того, что делает ваш код. Это не так просто, как если бы я использовал этот код, я в безопасности . Это очень минимальный и полубезопасный способ синхронного запуска асинхронных команд, его можно использовать ненадлежащим образом, чтобы вызвать взаимные блокировки.
— Эрик Филипс

1
Спасибо. 2 дополнительных вопроса: 1) Можете ли вы привести пример того, чего асинхронный метод хочет избежать, что может привести к взаимоблокировке, и 2) являются ли взаимоблокировки в этом контексте часто зависящими от времени? Если это работает на практике, могу ли я по-прежнему зависать в моем коде в зависимости от времени?
— Bob.at.Indigo.Health

@ Bob.at.SBS Я бы порекомендовал задать вопрос, используя кнопку Задать вопрос в правом верхнем углу. Вы можете включить ссылку на этот вопрос или ответ в свой вопрос в качестве ссылки.
— Эрик Филипс

1
@ Bob.at ... код, предоставленный Эриком, прекрасно работает под Asp. net mvc5 и EF6, но не тогда, когда я попробовал какое-либо из других решений (ConfigureAwait (false) .GetAwaiter (). GetResult () или .result), которое полностью зависает в моем веб-приложении
— LeonardoX

151

async Main теперь является частью C # 7.2 и может быть включен в расширенных настройках проекта.

Для C # <7.2 правильный путь:

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}

Вы увидите, что это используется во многих документах Microsoft, например: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- Тем-подписка


11
Понятия не имею, ПОЧЕМУ кто-то проголосовал за это. Это отлично сработало для меня. Без этого исправления мне пришлось бы распространять АСЫХ ВЕЗДЕ.
— Заключенный НОЛЬ

11
Почему это лучше чем MainAsync().Wait()?
— раздавить

8
Согласен. Вам просто нужно MainAsync (). Wait () вместо всего этого.
— Хаджат

8
@crush Я описывал, как это может избежать некоторых тупиков. В некоторых ситуациях вызов .Wait () из пользовательского интерфейса или потока asp.net вызывает тупик. асинхронные тупики
— Дэвид

6
@ClintB: Вы не должны делать это в ASP.NET Core. Веб-приложения особенно уязвимы для того, чтобы быть истощенными потоками, и каждый раз, когда вы делаете это, вы извлекаете поток из пула, который в противном случае использовался бы для обслуживания запроса. Это менее проблематично для настольных / мобильных приложений, поскольку они традиционно являются однопользовательскими.
— Крис Пратт

52
public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

Вы читаете ключевое слово «await» как «запустите эту долгосрочную задачу, а затем верните управление вызывающему методу». Как только долгосрочное задание выполнено, оно выполняет код после него. Код после await похож на то, что раньше было методами CallBack. Большая разница в том, что логический поток не прерывается, что делает его намного легче писать и читать.


15
WaitОборачивает исключения и имеет возможность тупика.
— Стивен Клири

Я думал, что если вы вызываете асинхронный метод без использования await, он будет выполняться синхронно. По крайней мере, это работает для меня (без звонка myTask.Wait). На самом деле, я получил исключение, когда попытался позвонить, myTask.RunSynchronously()потому что он уже был выполнен!
— Авр

2
Мне нравится этот ответ. Хорошие комментарии для редактирования, маленькие и элегантные. Спасибо за помощь! Я все еще учусь параллелизму, поэтому все помогает :)
— kayleeFrye_onDeck

2
Должен ли этот ответ работать до сих пор? Я только что попробовал это в проекте MVC Razor, и приложение просто зависает при доступе .Result.
— Ушел кодирование

8
@TrueBlueAussie Это тупик контекста синхронизации. Ваш асинхронный код возвращается к контексту синхронизации, но Resultв тот момент вызов блокировался , поэтому он никогда не попадал туда. И Resultникогда не кончается, потому что он ждет того, кто ждет Resultконца, в основном: D
— Luaan

40

Я не уверен на 100%, но я считаю, что методика, описанная в этом блоге, должна работать во многих случаях:

Таким образом, вы можете использовать, task.GetAwaiter().GetResult()если хотите напрямую вызывать эту логику распространения.


6
Решение A в ответе Стивена Клири выше использует этот метод. См. Источник WaitAndUnwrapException .
— Орад

вам нужно использовать GetResult (), если функция, которую вы вызываете, void или task? Я имею в виду, если вы не хотите получать какие-либо результаты обратно
— batmaci

Да, иначе он не будет блокироваться до завершения задачи. В качестве альтернативы вместо вызова GetAwaiter (). GetResult () вы можете вызвать .Wait ()
— NStuke

1
Это часть "многих обстоятельств". Это зависит от общей модели потоков и от того, что другие потоки делают, чтобы определить, существует ли риск взаимоблокировки или нет.
— NStuke

GetAwaiter (). GetResult () все еще может вызывать взаимоблокировки. Это только разворачивает исключение в более разумное.
— nawfal

25

Однако есть хорошее решение, которое работает (почти: см. Комментарии) в любой ситуации: специальный механизм обработки сообщений (SynchronizationContext).

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

Код специального помощника по отправке сообщений:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Threading
{
    /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
    public static class AsyncPump
    {
        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Action asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(true);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function
                syncCtx.OperationStarted();
                asyncMethod();
                syncCtx.OperationCompleted();

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Func<Task> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static T Run<T>(Func<Task<T>> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                return t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
        private sealed class SingleThreadSynchronizationContext : SynchronizationContext
        {
            /// <summary>The queue of work items.</summary>
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
            /// <summary>The processing thread.</summary>
            private readonly Thread m_thread = Thread.CurrentThread;
            /// <summary>The number of outstanding operations.</summary>
            private int m_operationCount = 0;
            /// <summary>Whether to track operations m_operationCount.</summary>
            private readonly bool m_trackOperations;

            /// <summary>Initializes the context.</summary>
            /// <param name="trackOperations">Whether to track operation count.</param>
            internal SingleThreadSynchronizationContext(bool trackOperations)
            {
                m_trackOperations = trackOperations;
            }

            /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
            /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
            /// <param name="state">The object passed to the delegate.</param>
            public override void Post(SendOrPostCallback d, object state)
            {
                if (d == null) throw new ArgumentNullException("d");
                m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
            }

            /// <summary>Not supported.</summary>
            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("Synchronously sending is not supported.");
            }

            /// <summary>Runs an loop to process all queued work items.</summary>
            public void RunOnCurrentThread()
            {
                foreach (var workItem in m_queue.GetConsumingEnumerable())
                    workItem.Key(workItem.Value);
            }

            /// <summary>Notifies the context that no more work will arrive.</summary>
            public void Complete() { m_queue.CompleteAdding(); }

            /// <summary>Invoked when an async operation is started.</summary>
            public override void OperationStarted()
            {
                if (m_trackOperations)
                    Interlocked.Increment(ref m_operationCount);
            }

            /// <summary>Invoked when an async operation is completed.</summary>
            public override void OperationCompleted()
            {
                if (m_trackOperations &&
                    Interlocked.Decrement(ref m_operationCount) == 0)
                    Complete();
            }
        }
    }
}

Применение:

AsyncPump.Run(() => FooAsync(...));

Более подробное описание асинхронного насоса доступно здесь .


Контекст исключения и AsyncPump stackoverflow.com/questions/23161693/…
— PreguntonCojoneroCabrón

Это не работает в сценарии Asp.net, так как вы можете случайно потерять HttpContext.Current.
— Джош Моуч

12

Всем, кто больше обращает внимание на этот вопрос ...

Если вы посмотрите, Microsoft.VisualStudio.Services.WebApiесть класс с именем TaskExtensions. В этом классе вы увидите статический метод расширенияTask.SyncResult() , который, как и полностью, просто блокирует поток до тех пор, пока задача не вернется.

Внутренне он вызывает task.GetAwaiter().GetResult()довольно просто, но перегружен для работы с любым asyncвозвращаемым методом Task, Task<T>илиTask<HttpResponseMessage> ... синтаксический сахар, ребенок ... папа получил сладкий зуб.

Похоже, ...GetAwaiter().GetResult()это официальный MS-способ выполнения асинхронного кода в контексте блокировки. Кажется, работает очень хорошо для моего случая использования.


3
Вы были со мной "как просто блоки".
— Дауд ибн Карим

9
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

Или используйте это:

var result=result.GetAwaiter().GetResult().AccessToken

6

Вы можете вызывать любой асинхронный метод из синхронного кода, то есть до тех пор, пока он вам не понадобится await, и в этом случае они также должны быть отмечены async.

Как многие здесь предлагают, вы можете вызвать Wait () или Result для результирующей задачи в вашем синхронном методе, но затем вы получите блокирующий вызов в этом методе, что побеждает асинхронную цель.

Если вы действительно не можете создать свой метод asyncи не хотите блокировать синхронный метод, тогда вам придется использовать метод обратного вызова, передав его в качестве параметра методу ContinueWith в задаче.


5
Тогда это не будет вызывать метод синхронно, не так ли?
— Джефф Меркадо

2
Как я понимаю, вопрос был в том, можно ли вызвать асинхронный метод из неасинхронного метода. Это не подразумевает необходимость вызова асинхронного метода блокирующим образом.
— base2

Извините, ваши «они тоже должны быть отмечены async» отвлекли мое внимание от того, что вы на самом деле говорили.
— Джефф Меркадо

Если меня не волнует асинхронность, можно ли так ее называть (и как насчет возможности взаимоблокировок в обернутых исключениях, из-за которых Стивен Клири все время ворчит?) У меня есть несколько тестовых методов (которые должны выполняться синхронно) это проверяет асинхронные методы. Я должен дождаться результата, прежде чем продолжить, чтобы я мог проверить результат асинхронного метода.
— благоговение

6

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

Я нашел следующий кусок кода от Райана

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

    /// <summary>
    /// Executes an async Task method which has a void return value synchronously
    /// USAGE: AsyncUtil.RunSync(() => AsyncMethod());
    /// </summary>
    /// <param name="task">Task method to execute</param>
    public static void RunSync(Func<Task> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    /// <summary>
    /// Executes an async Task<T> method which has a T return type synchronously
    /// USAGE: T result = AsyncUtil.RunSync(() => AsyncMethod<T>());
    /// </summary>
    /// <typeparam name="TResult">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static TResult RunSync<TResult>(Func<Task<TResult>> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

тогда вы можете назвать это так

var t = AsyncUtil.RunSync<T>(() => AsyncMethod<T>());

6
Это похоже на приведенный выше ответ : я что-то
— упустил

2

После нескольких часов проб разных методов, с более или менее успешным завершением, на этом я и закончил. Он не заканчивается тупиком при получении результата, а также получает и выдает исходное исключение, а не завернутое.

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

1
Работает с return task.GetAwaiter (). GetResult ();
— Per G

да, но как насчет оригинального исключения?
— Йиржи Эрник

.Результат, на мой взгляд, тот же, что и .GetAwaiter (). GetResult ()
— за G

-2

Он может быть вызван из нового потока (НЕ из пула потоков!):

public static class SomeHelperClass
{ 
       public static T Result<T>(Func<T> func)
        {
            return Task.Factory.StartNew<T>(
                  () => func()
                , TaskCreationOptions.LongRunning
                ).Result;
        }
}
...
content = SomeHelperClass.Result<string>(
  () => response.Content.ReadAsStringAsync().Result
  );

-3

Эти асинхронные методы Windows имеют изящный маленький метод, называемый AsTask (). Вы можете использовать это, чтобы метод возвращал себя в качестве задачи, чтобы вы могли вручную вызывать Wait () для него.

Например, в приложении Windows Phone 8 Silverlight вы можете сделать следующее:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

Надеюсь это поможет!


-4

Если вы хотите запустить его Sync

MethodAsync().RunSynchronously()

3
Этот метод предназначен для запуска холодных задач. Обычно асинхронные методы возвращают горячую задачу, другими словами, задачу, которая уже запущена. вызов RunSynchronously()горячей задачи приводит к InvalidOperationException. Попробуйте это с этим кодом:Task.Run(() => {}).RunSynchronously();
— Теодор Zoulias

-5
   //Example from non UI thread -    
   private void SaveAssetAsDraft()
    {
        SaveAssetDataAsDraft();
    }
    private async Task<bool> SaveAssetDataAsDraft()
    {
       var id = await _assetServiceManager.SavePendingAssetAsDraft();
       return true;   
    }
   //UI Thread - 
   var result = Task.Run(() => SaveAssetDataAsDraft().Result).Result;

2
Создать тупик. Лучше удали ответ.
— PreguntonCojoneroCabrón

Task.Run (() => SaveAssetDataAsDraft ()). Result; - не создает тупик
— Anubis
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.