Как мне синхронно запустить асинхронный метод Task ‹T›?

Я изучаю async / await и столкнулся с ситуацией, когда мне нужно вызвать метод async синхронно. Как я могу это сделать?

Асинхронный метод:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

Нормальное использование:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

Я пробовал использовать следующее:

Task<Customer> task = GetCustomers();
task.Wait()

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

Я также попробовал предложение здесь, но не работает, когда диспетчер находится в приостановленном состоянии.

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

Вот исключение и трассировка стека от вызова RunSynchronously:

System.InvalidOperationException

Сообщение: RunSynchronously нельзя вызывать для задачи, не связанной с делегатом.

InnerException: null

Источник: mscorlib

StackTrace:

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

person Rachel    schedule 23.02.2011    source источник
comment
Лучший ответ на вопрос «Как я могу вызвать асинхронный метод синхронно» - нет. Есть хаки, чтобы заставить его работать, но все они имеют очень тонкие подводные камни. Вместо этого сделайте резервную копию и исправьте код, который заставляет вас это делать.   -  person Stephen Cleary    schedule 15.10.2013
comment
@Stephen Cleary Абсолютно согласен, но иногда это просто неизбежно, например, когда ваш код зависит от некоторого стороннего API, который не использует async / await. Кроме того, при привязке к свойствам WPF при использовании MVVM буквально невозможно использовать async / await, поскольку это не поддерживается для свойств.   -  person Contango    schedule 12.09.2014
comment
@StephenCleary Не всегда. Я создаю DLL, которая будет импортирована в GeneXus. Он не поддерживает ключевые слова async / await, поэтому я должен использовать только синхронные методы.   -  person Dinei    schedule 26.10.2015
comment
@ DineiA.Rockenbach: Сразу приходят на ум три лучшие альтернативы: 1) добавить в GeneXus поддержку асинхронных методов; 2) Реализуйте обратный вызов / событие вместо использования Task (часто требуется для межъязыкового взаимодействия); 3) Используйте синхронный код полностью, чтобы синхронизация через асинхронность даже не возникла. Каждый из них - лучшая альтернатива сомнительным взломам.   -  person Stephen Cleary    schedule 26.10.2015
comment
@StephenCleary 1) GeneXus - это третий инструмент, и у меня нет доступа к его исходному коду; 2) GeneXus даже не имеет реализации функций, поэтому я не могу понять, как я могу реализовать обратный вызов с помощью этого типа вещей. Конечно, это будет более сложный обходной путь, чем синхронное использование Task; 3) Я интегрирую GeneXus с MongoDB C # driver, который предоставляет только некоторые методы асинхронно   -  person Dinei    schedule 26.10.2015
comment
@StephenCleary Это все хорошая теория, но не делайте этого, потому что она не работает. C # активно запрещает мне использовать await в синхронизированных блоках. Следует ли мне попросить Microsoft изменить свой язык? Или мне следует отказаться от синхронизации и принять испорченные структуры данных? async это рак, а не GPL. Если у вас есть это, вы не сможете от него избавиться.   -  person ygoe    schedule 26.09.2016
comment
@ygoe: используйте асинхронную блокировку, например SemaphoreSlim.   -  person Stephen Cleary    schedule 26.09.2016
comment
Отсутствие StackOverflow с самыми громкими именами даже в конце 2016 года убедительно свидетельствует о том, что до сих пор нет простого способа сделать это. КСТАТИ. Интересно, почему нет оператора runsync.   -  person tymtam    schedule 19.11.2016
comment
Возможный дубликат Как вызвать асинхронный метод из синхронного метода в C #?   -  person Michael Freidgeim    schedule 02.02.2017
comment
Связывание связанных вопросов и ответов о том, как это сделать в потоке пользовательского интерфейса.   -  person noseratio    schedule 29.11.2018


Ответы (24)


Я нашел обходной путь, который работает во всех случаях (включая приостановленных диспетчеров). Это не мой код, и я все еще работаю над его пониманием, но он работает.

Его можно вызвать с помощью:

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

Код взят из здесь

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}
person Rachel    schedule 23.02.2011
comment
Чтобы получить некоторую справочную информацию о том, как это работает, Стивен Туб (мистер Параллель) написал серию сообщений об этом. Часть 1 Часть 2 Часть 3 - person Cameron MacFarland; 08.02.2013
comment
Я обновил код Джона, чтобы он работал без упаковки задач в лямбды: github.com/tejacques/AsyncBridge. По сути, вы работаете с асинхронными блоками с помощью оператора using. Все внутри блока using происходит асинхронно с ожиданием в конце. Обратной стороной является то, что вам нужно самостоятельно развернуть задачу в обратном вызове, но это все равно довольно элегантно, особенно если вам нужно вызвать сразу несколько асинхронных функций. - person Tom Jacques; 25.06.2013
comment
Этот метод блокирует поток пользовательского интерфейса, в котором он выполняется. Можно ли каким-то образом использовать метод Dispatcher.PushFrame(DispatcherFrame) для предотвращения блокировки пользовательского интерфейса? - person ghord; 11.07.2013
comment
@ghord - если вы не хотите блокировать пользовательский интерфейс, вы не можете запускать его синхронно ... по определению он блокирует поток, в котором он работает. Просто подождите, если не хотите, чтобы он блокировался. Я не понимаю, о чем вы спрашиваете. - person Mike Marynowski; 15.07.2013
comment
@MikeMarynowski У меня возникла проблема с методом async void (невозможно изменить на Task) с ожиданиями, которые я хотел выполнить полностью перед передачей управления. Это решение помогает, но вешает поток пользовательского интерфейса, чего я пытался избежать. - person ghord; 16.07.2013
comment
Однако то, о чем вы просите, невозможно, просто подумайте - если вы не передадите управление обратно потоку пользовательского интерфейса, пользовательский интерфейс не будет обновляться. По определению, что-то, что выполняется синхронно в потоке пользовательского интерфейса, замораживает пользовательский интерфейс до его завершения. Я уверен, что есть лучший способ добиться того, что вы пытаетесь сделать. Вместо того, чтобы раздувать эту тему еще больше, как насчет того, чтобы вы разместили отдельный вопрос, и я постараюсь ответить на него изо всех сил. - person Mike Marynowski; 16.07.2013
comment
@MikeMarynowski Это было возможно в 4.0 с Dispatcher.PushFrame, и это работало довольно хорошо. Так уж получилось, что это приводит к взаимоблокировке кода async / await. Мне не нужен новый вопрос - я просто реорганизовал свое приложение, чтобы оно не понадобилось. - person ghord; 17.07.2013
comment
@StephenCleary Хотя я в целом согласен с вами, что код должен быть полностью асинхронным, иногда вы попадаете в невыполнимую ситуацию, когда один должен принудительно использовать его как синхронный вызов. По сути, моя ситуация такова, что весь мой код доступа к данным является асинхронным. Мне нужно было создать карту сайта на основе карты сайта, и я использовал стороннюю библиотеку MvcSitemap. Теперь, когда его расширяют с помощью базового класса DynamicNodeProviderBase, его нельзя объявить как метод async. Либо пришлось заменить на новую библиотеку, либо просто вызвать синхронную операцию. - person justin.lovell; 23.01.2014
comment
@ justin.lovell: Да, ограничения библиотеки могут заставить нас использовать хаки, по крайней мере, до тех пор, пока библиотека не будет обновлена. Похоже, что MvcSitemap - одна из таких ситуаций, когда требуется взлом (фильтры MVC и дочерние действия тоже); Я просто отговариваю людей от этого в целом, потому что подобные хаки используются слишком часто, когда они не необходимы. В частности, с MVC некоторые API-интерфейсы ASP.NET/MVC предполагают, что у них есть AspNetSynchronizationContext, поэтому этот конкретный прием не сработает, если вы вызываете эти API-интерфейсы. - person Stephen Cleary; 23.01.2014
comment
@ghord конечно, он по-прежнему работает отлично: gist.github.com/ivan-danilov/3ee430522d596ccf6496 - person Ivan Danilov; 19.10.2015
comment
@Paval Этот ответ довольно старый, однако в прошлом у меня никогда не было подобных проблем с кодом. Использование ключевого слова await говорит, что помещает эту команду в очередь в отдельном потоке, и все, что ниже этого, должно выполняться после завершения потока, затем завершить этот блок кода и вернуться к тому, что вы делали еще, что в этом case - это цикл сообщений, повторно вызывающий .WaitOne. Поскольку задача выполняется в отдельном потоке, я не думаю, что вы должны испытывать мертвые блокировки с этим кодом. - person Rachel; 07.07.2016
comment
Иногда вы наследуете чужой код, который был написан полностью синхронно, и вам нужно что-то доставить к определенному сроку, и переписывать все так, чтобы оно было асинхронным, вверх - не вариант. - person Sentinel; 08.09.2016
comment
В этом коде есть ошибка. Он не восстанавливает старый контекст (SynchronizationContext.SetSynchronizationContext(oldContext)) при возникновении исключения. Использование try { ... } finally { ... } исправляет это. - person Michael Kropat; 05.10.2016
comment
Чтобы избежать предупреждения FxCopy, реализуйте IDisposable в ExclusiveSynchronizationContext, удалив _workItemsWaiting - person mqueirozcorreia; 05.11.2016
comment
К сожалению, это не работает для всех случаев. WaitOne () внутренне перекачивает сообщения (так что, как мне кажется, COM работает должным образом). Это означает, что внешний код (например, обработчики событий) может выполняться каждый раз, когда задача ожидается в настраиваемом контексте, и вызывается WaitOne (). У меня возникла такая ситуация, вызывающая взаимоблокировку между основным потоком и им самим: / Я решил это, выполнив свою задачу в новом потоке и фактически заблокировав while (!task.IsCompleted) { /*wait*/}. Это уродливо, но если что-то в вашей задаче не пытается выполнить в ожидающем потоке, все должно работать. - person Melvyn; 21.02.2018
comment
@TomJacques пробовал вашу библиотеку, так как я подозревал, что она работает без использования лямбда-выражений. Пробовал с небольшим приложением winforms. У меня не сработало. Конечно, я тестировал горячую задачу. - person nawfal; 04.08.2020
comment
У меня проблемы с моим консольным приложением, которое просто зависает, не знаю почему, но после использования этого кода это начало происходить. - person Mike Flynn; 11.10.2020
comment
Хотя этот ответ был опубликован очень давно. Большое спасибо, после серии поисков это сработало однажды. - person Mordecai; 11.03.2021
comment
В некоторых редких случаях ExclusiveSynchronizationContext.Post () может быть вызван после выхода из BeginMessageLoop (), и обратный вызов никогда не будет выполнен. Пример: AsyncHelpers.RunSync (() = ›MyTask ()); приватная статическая асинхронная задача MyTask () {ожидание Task.Delay (1); var scheduler = TaskScheduler.FromCurrentSynchronizationContext (); Task.Run (async () = ›await Task.Delay (1000)). ContinueWith (g =› {Console.WriteLine (никогда не будет выполняться);}, планировщик); ждать Task.Delay (100); } - person Rakoo; 23.06.2021

Обратите внимание, этому ответу уже три года. Я написал его, основываясь в основном на опыте работы с .Net 4.0 и очень немногое с 4.5, особенно с async-await. Вообще говоря, это хорошее простое решение, но иногда оно ломается. Пожалуйста, прочтите обсуждение в комментариях.

.Net 4.5

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

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

См .: TaskAwaiter, Task.Result, Task.RunSynchronously


.Net 4.0

Использовать этот:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...или это:

task.Start();
task.Wait();
person AK_    schedule 02.08.2012
comment
Если вы хотите использовать async в .NET 4.0, вы можете установить пакет NuGet async: nuget .org / packages / Microsoft.Bcl.Async - person class; 24.07.2013
comment
.Result может привести к тупиковой ситуации в определенных сценариях - person Jordy Langen; 22.08.2013
comment
@JordyLangen, конечно, вы ждете завершения этой задачи ... Но это обычно для параллельного программирования и всегда верно, когда чего-то ждете ... - person AK_; 29.08.2013
comment
Result может легко вызвать тупик в async коде , как я описываю в своем блоге. - person Stephen Cleary; 05.09.2013
comment
@StephenCleary Я прочитал ваш пост и попробовал сам. Я искренне думаю, что кто-то в Microsoft был действительно пьян ... Это та же проблема, что и с winforms и фоновыми потоками ... - person AK_; 15.10.2013
comment
task.Wait () иногда не работает синхронно. Это вводит несколько очень неприятных ошибок - person Cortlendt; 28.02.2014
comment
Вопрос касается Задачи, возвращаемой асинхронным методом. Такая задача могла быть уже запущена, выполнена или отменена, поэтому использование метода Task.RunSynchronously может привести к InvalidOperationException. См. Страницу MSDN: Метод Task.RunSynchronously. Кроме того, эта задача, вероятно, создается методами Task.Factory.StartNew или Task.Run (внутри асинхронного метода), поэтому пытаться запускать ее снова опасно. Некоторые состояния гонки могут возникать во время выполнения. С другой стороны, Task.Wait и Task.Result могут привести к тупиковой ситуации. - person sgnsajgon; 13.09.2014
comment
Синхронный запуск сработал для меня ... Я не знаю, упускаю ли я что-то, но это кажется предпочтительнее ужасов отмеченного ответа - я просто искал способ отключить асинхронный режим для тестирования кода, который просто должен остановиться пользовательский интерфейс от зависания - person JonnyRaa; 25.09.2014
comment
Result вызывает странные исключения подключения в Entity Framework - person Justin Skiles; 04.03.2015
comment
Когда вы запускаете это, возникает следующая ошибка: RunSynchronously не может быть вызван для задачи, не привязанной к делегату, такой как задача, возвращенная из асинхронного метода. - person Mohammad Chamanpara; 16.08.2015
comment
В документации для RunSynchronously () предлагается вызвать послесловие Wait () для обработки любых исключений, которые может вызвать задача. - person blackboxlogic; 26.09.2017
comment
Стоит отметить, что на основе this и this, тупик - не проблема для ASP.NET Core! - person S.Serpooshan; 12.03.2019
comment
а. RunSynchronously нельзя вызывать для задачи, не привязанной к делегату, поэтому я заключил ее в Task (async () = ›await methodAsync (). ConfigureAwait (continueOnCapturedContext: false)) b. Добавлен .Wait () в статье MSDN. Сюрприз: задача запустила синхронное выполнение, а затем потребовала завершения при запуске ввода-вывода, что привело к сбивающему с толку состоянию гонки. Вернулся к .GetAwaiter (). GetResult (); который действительно ждал завершения. Почему RunSynchronously () и Wait () возвращаются до того, как задача async действительно выполнена ??? - person David Burg; 25.06.2019

Удивленный, никто об этом не упомянул:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

Не так красиво, как некоторые другие методы здесь, но имеет следующие преимущества:

Кроме того, поскольку GetAwaiter имеет тип «утка», это должно работать для любого объекта, возвращаемого асинхронным методом (например, ConfiguredAwaitable или YieldAwaitable), а не только для задач.


edit: Обратите внимание, что этот подход (или использование .Result) может зайти в тупик, если вы не добавляете .ConfigureAwait(false) каждый раз, когда ожидаете, для всех асинхронных методов, которые могут быть доступны из BlahAsync() (не только те, которые он вызывает напрямую). Объяснение.

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

Если вам лень добавлять .ConfigureAwait(false) повсюду, и вас не волнует производительность, вы можете в качестве альтернативы сделать

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
person James Ko    schedule 07.02.2016
comment
У меня работает для простых вещей. Кроме того, если метод возвращает IAsyncOperation, мне нужно было сначала преобразовать его в Task: BlahAsync (). AsTask (). GetAwaiter (). GetResult (); - person Lee McPherson; 07.04.2016
comment
Это привело к тупиковой ситуации внутри веб-метода asmx. Тем не менее, включение вызова метода в Task.Run () заставило его работать: Task.Run (() = ›BlahAsync ()). GetAwaiter (). GetResult () - person Augusto Barreto; 13.12.2017
comment
Мне больше всего нравится этот подход синтаксически, потому что он не включает лямбды. - person dythim; 30.01.2018
comment
Пожалуйста, НЕ редактируйте ответы других людей, чтобы вставить ссылку на свои собственные. Если вы считаете, что ваш ответ лучше, оставьте его как комментарий. - person Rachel; 02.03.2018
comment
Должен быть решением вопроса, поскольку он решает проблему, минимальный пользовательский код, а ответ прекрасно объясняет ключевые моменты. - person Eugene Y.; 26.03.2018
comment
docs.microsoft.com/en-us/dotnet/api/ говорит о GetAwaiter(), этот метод предназначен для компилятора пользователем, а не использовать непосредственно в коде. - person Theophilus; 04.09.2019
comment
Таким образом (BlahAsync ()). GetAwaiter (). GetResult ()) может привести к зависанию программы и никогда не завершиться. Вот почему мне пришлось переключиться на Rachel's, чтобы добиться большей производительности. - person DotNet Programmer; 19.02.2020

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

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 
person Michael L Perry    schedule 13.06.2013
comment
А если задача недействительна (без результата)? - person J. Lennon; 07.10.2013
comment
Затем вы вызываете task.Wait (). Тип данных - это просто Task. - person Michael L Perry; 09.04.2014
comment
Предположим, что DoSomethingAsync () - это длительный асинхронный метод в целом (внутри он ожидает длительную задачу), но он быстро возвращает управление потоком вызывающей стороне, поэтому работа лямбда-аргумента заканчивается тоже быстро. Результат Tusk.Run () может быть Task ‹Task› или Task ‹Task‹ ››, поэтому вы ожидаете результата внешней задачи, которая выполняется быстро, но внутренняя задача (из-за ожидания длительного задания в асинхронном методе) все еще выполняется. Выводы состоят в том, что нам, вероятно, потребуется использовать подход Unwrap () (как это было сделано в сообщении @J.Lennon) для достижения синхронного поведения метода async. - person sgnsajgon; 13.09.2014
comment
@sgnsajgon Вы ошибаетесь. Task.Run отличается от Task.Factory.StartNew тем, что автоматически разворачивает уже полученный результат. См. эту статью. - person ZunTzu; 02.11.2015
comment
Могу я вместо этого просто написать Task.Run(DoSomethingAsync)? Это удаляет один уровень делегатов. - person ygoe; 13.12.2017
comment
Ага. Однако движение в противоположном направлении, как в Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());, является более явным и устраняет опасения @sgnsajgon о том, что он может возвращать Task ‹Task ‹MyResult››. В любом случае выбирается правильная перегрузка Task.Run, но асинхронный делегат делает ваше намерение очевидным. - person Michael L Perry; 21.02.2018

Я изучаю async / await и столкнулся с ситуацией, когда мне нужно вызвать метод async синхронно. Как я могу это сделать?

Лучший ответ - нет, причем детали зависят от "ситуации".

Это средство получения / установки свойства? В большинстве случаев лучше иметь асинхронные методы, чем «асинхронные свойства». (Для получения дополнительной информации см. мою запись в блоге об асинхронных свойствах).

Это приложение MVVM, и вы хотите выполнять асинхронную привязку данных? Затем используйте что-то вроде моего NotifyTask, как описано в моем Статья MSDN об асинхронной привязке данных.

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

Почти всегда есть лучший ответ, чем синхронизация через асинхронность.

Если в вашей ситуации это невозможно (и вы знаете это, задав здесь вопрос, описывая ситуацию), то я бы рекомендовал просто использовать синхронный код. Лучше всего асинхронный режим; Синхронизация на всем пути занимает второе место. Асинхронная синхронизация не рекомендуется.

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

В этом случае вам нужно будет использовать один из приемов, описанных в моей статье о brownfield async разработка, в частности:

  • Блокировка (например, GetAwaiter().GetResult()). Обратите внимание, что это может вызвать взаимоблокировки (как я опишу в моем блоге).
  • Запуск кода в потоке пула потоков (например, Task.Run(..).GetAwaiter().GetResult()). Обратите внимание, что это будет работать только в том случае, если асинхронный код может быть запущен в потоке пула потоков (т. Е. Не зависит от пользовательского интерфейса или контекста ASP.NET).
  • Вложенные циклы сообщений. Обратите внимание, что это будет работать только в том случае, если асинхронный код предполагает только однопоточный контекст, а не конкретный тип контекста (большая часть кода пользовательского интерфейса и ASP.NET ожидает определенного контекста).

Вложенные циклы сообщений являются наиболее опасными из всех взломов, поскольку они вызывают повторный вход . О повторном входе чрезвычайно сложно рассуждать, и (IMO) является причиной большинства ошибок приложений в Windows. В частности, если вы находитесь в потоке пользовательского интерфейса и блокируете рабочую очередь (ожидая завершения асинхронной работы), тогда CLR фактически выполняет за вас некоторую перекачку сообщений - она ​​фактически обрабатывает некоторые сообщения Win32 из вашего кода. Да, и вы понятия не имеете, какие сообщения - когда Chris Brumme говорит:« Разве не было бы здорово знать, что именно будет прокачиваться? К сожалению, прокачка - это черное искусство, которое недоступно для понимания смертных ». у нас действительно нет надежды узнать.

Итак, когда вы блокируете подобным образом поток пользовательского интерфейса, вы напрашиваетесь на проблемы. Еще одна цитата cbrumme из той же статьи: «Время от времени клиенты внутри или вне компании обнаруживают, что мы перекачиваем сообщения во время управляемой блокировки в STA [потоке пользовательского интерфейса]. Это законное беспокойство, потому что они знают, что это очень сложно написать код, устойчивый к повторному входу ".

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

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

Если вы все же оказались в этом углу, я бы порекомендовал использовать что-то вроде Dispatcher.PushFrame для Приложения WPF, цикл с Application.DoEvents для приложений WinForm и, в общем случае, мой собственный _7 _ .

person Stephen Cleary    schedule 07.09.2016
comment
Стивен, есть еще один очень похожий вопрос. на который вы тоже дали отличный ответ. Как вы думаете, один из них может быть закрыт как дубликат, или, может быть, запрос на слияние, или сначала вывести на мета (поскольку каждый q имеет ~ 200K просмотров, 200+ голосов)? Предложения? - person Alexei Levenkov; 05.02.2017
comment
@AlexeiLevenkov: Я не чувствую себя правильным делать это по нескольким причинам: 1) Ответ на связанный вопрос довольно устарел. 2) Я написал целую статью на эту тему, которую я чувство более полно, чем любой существующий SO Q / A. 3) Принятый ответ на этот вопрос чрезвычайно популярен. 4) Я категорически против этого принятого ответа. Так что закрытие этого как дубликата было бы злоупотреблением властью; закрытие этого как дублирования этого (или слияния) даст еще больше возможностей для опасного ответа. Я оставлю это сообществу. - person Stephen Cleary; 05.02.2017
comment
Ok. Я подумаю о том, чтобы поднять это на мета, чем в каком-то смысле. - person Alexei Levenkov; 06.02.2017
comment
Этот ответ проходит далеко не у меня в голове. Использовать async до конца - это сбивающий с толку совет, потому что ему явно невозможно следовать. Программа с асинхронным Main() методом не компилируется; в какой-то момент вы должны преодолеть разрыв между мирами синхронизации и асинхронности. Это не очень редкая ситуация, это необходимо буквально в каждой программе, которая вызывает асинхронный метод. Нет возможности не выполнять синхронизацию через асинхронность, просто есть возможность переложить эту нагрузку на вызывающий метод вместо того, чтобы переложить ее на тот, который вы сейчас пишете. - person Mark Amery; 13.06.2017
comment
@MarkAmery: синхронизация через асинхронность необходима в Main методе консольных приложений. ASP.NET, платформы модульного тестирования и каждая система пользовательского интерфейса изначально поддерживают асинхронность. Даже если все ваши приложения являются консольными, вам нужно будет выполнить синхронизацию через асинхронность только один раз для каждого приложения. (конечно, обратные вызовы библиотеки, которые еще не поддерживают асинхронность, могут потребовать дополнительных хаков). - person Stephen Cleary; 13.06.2017
comment
Большой. Я собираюсь добавить async во все методы в моем приложении. А это много. Разве это не может быть просто по умолчанию? - person ygoe; 13.12.2017
comment
Стоит отметить, что как вы ответил мне на основе вашего блога здесь, тупик - не проблема для ASP.NET Core! - person S.Serpooshan; 12.03.2019

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

Асинхронные методы в C # 5 основаны на эффективном разделении метода на части под капотом и возвращении Task, который может отслеживать общее завершение всего шабанга. Однако то, как выполняются разделенные методы, может зависеть от типа выражения, переданного оператору await.

В большинстве случаев вы будете использовать await в выражении типа Task. Реализация в задаче шаблона await "умна" в том смысле, что она подчиняется SynchronizationContext, что в основном приводит к следующему:

  1. Если поток, входящий в await, находится в потоке цикла сообщений Dispatcher или WinForms, это гарантирует, что фрагменты асинхронного метода выполняются как часть обработки очереди сообщений.
  2. Если поток, входящий в await, находится в потоке пула потоков, тогда оставшиеся фрагменты асинхронного метода происходят где угодно в пуле потоков.

Вот почему вы, вероятно, столкнетесь с проблемами - реализация асинхронного метода пытается запустить остальное на Диспетчере, даже если он приостановлен.

.... резервное копирование! ....

Я должен задать вопрос: почему вы пытаетесь синхронно блокировать асинхронный метод? Это нарушит цель, почему метод хотел вызываться асинхронно. В общем, когда вы начинаете использовать await в диспетчере или методе пользовательского интерфейса, вы захотите включить весь поток пользовательского интерфейса в асинхронный режим. Например, если ваш стек вызовов был примерно таким:

  1. [Вверх] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF или WinForms Код
  6. [Цикл сообщений] - WPF или WinForms Цикл сообщений

Затем, как только код будет преобразован для использования async, вы, как правило, получите

  1. [Вверх] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF или WinForms Код
  6. [Цикл сообщений] - WPF или WinForms Цикл сообщений

Фактически отвечаю

Вышеупомянутый класс AsyncHelpers фактически работает, потому что он ведет себя как вложенный цикл сообщений, но он устанавливает свой собственный параллельный механизм для Dispatcher, а не пытается выполнить на самом Dispatcher. Это один из способов решения вашей проблемы.

Другой обходной путь - выполнить асинхронный метод в потоке пула потоков, а затем дождаться его завершения. Сделать это просто - вы можете сделать это с помощью следующего фрагмента:

var customerList = TaskEx.RunEx(GetCustomers).Result;

Последним API будет Task.Run (...), но с CTP вам понадобятся суффиксы Ex (объяснение здесь).

person Theo Yaung    schedule 24.02.2011
comment
+1 за подробное объяснение, однако TaskEx.RunEx(GetCustomers).Result приложение зависает, когда оно запускается в приостановленном потоке диспетчера. Кроме того, метод GetCustomers () обычно запускается асинхронно, однако в одной ситуации он должен выполняться синхронно, поэтому я искал способ сделать это, не создавая синхронизирующую версию метода. - person Rachel; 25.02.2011
comment
+1 за то, что вы пытаетесь синхронно блокировать асинхронный метод? Всегда есть способ правильно использовать async методы; конечно, следует избегать вложенных циклов. - person Stephen Cleary; 05.09.2013

Это хорошо работает для меня

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}
person Clement    schedule 23.07.2014
comment
Вам также необходимо использовать метод Task.Unwrap, потому что ваш оператор Task.Wait вызывает ожидание внешней задачи (созданной Task.Run), не для внутреннего await t Задача, переданная как параметр метода расширения. Ваш метод Task.Run возвращает не Task ‹T›, а Task ‹Task ‹T››. В некоторых простых сценариях ваше решение может работать из-за оптимизации TaskScheduler, например, с использованием метода TryExecuteTaskInline для выполнения задач в текущем потоке во время операции Wait. Пожалуйста, посмотрите мой комментарий к этот ответ. - person sgnsajgon; 15.09.2014
comment
Это не так. Task.Run вернет Task ‹T›. См. Эту перегрузку msdn.microsoft.com/en- us / library / hh194918 (v = vs.110) .aspx - person Clement; 16.09.2014
comment
Как это использовать? Это тупиковые ситуации в WPF: MyAsyncMethod().RunTaskSynchronously(); - person ygoe; 26.09.2016
comment
Это работает только для платформ без контекстов синхронизации (консольные приложения, приложения ASP.NET Core и т. Д.). Для платформ с контекстом синхронизации это работает только для холодных задач, а не для 99% обычных случаев. Для уже запущенных задач нет смысла оборачивать Task.Run. Другими словами, при обычном использовании, например, GetFromNetworkAsync().RunTaskSynchronously() зависает для приложений пользовательского интерфейса. - person nawfal; 04.08.2020

Я сталкивался с этим несколько раз, в основном при модульном тестировании или при разработке службы Windows. В настоящее время я всегда использую эту функцию:

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

Это просто, легко и без проблем.

person J. Lennon    schedule 05.10.2013
comment
Это единственное, что не зашло для меня в тупик. - person AndreFeijo; 17.02.2017
comment
@AndreFeijo Я не знаю, что это, но по сути это Task.Run(() => ..).Wait() (с небольшими изменениями). Оба должны работать. - person nawfal; 04.08.2020

Самый простой способ, который я нашел для выполнения задачи синхронно и без блокировки потока пользовательского интерфейса, - это использовать RunSynchronously (), например:

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

В моем случае у меня есть событие, которое срабатывает, когда что-то происходит. Я не знаю, сколько раз это произойдет. Итак, я использую приведенный выше код в своем мероприятии, поэтому всякий раз, когда он срабатывает, он создает задачу. Задачи выполняются синхронно, и у меня это отлично работает. Я был просто удивлен, что мне потребовалось так много времени, чтобы выяснить это, учитывая, насколько это просто. Обычно рекомендации намного сложнее и подвержены ошибкам. Это было несложно и чисто.

person pixel    schedule 25.10.2016
comment
Но как мы могли использовать этот метод, если асинхронный код возвращает то, что нам нужно? - person S.Serpooshan; 10.09.2018
comment
Это работает для холодных задач, а не для начатых задач. - person nawfal; 04.08.2020

Я нашел этот код в компоненте Microsoft.AspNet.Identity.Core, и он работает.

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

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}
person wenhx    schedule 02.12.2014
comment

Небольшое замечание - этот подход:

Task<Customer> task = GetCustomers();
task.Wait()

работает для WinRT.

Позволь мне объяснить:

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

Причем этот подход работает только для решений для Магазина Windows!

Примечание. Этот способ небезопасен для потоков, если вы вызываете свой метод внутри другого асинхронного метода (согласно комментариям @Servy).

person RredCat    schedule 20.06.2012
comment
Я объяснил это решение, проверьте раздел EDIT. - person RredCat; 16.12.2013
comment
Это может очень легко привести к тупикам при вызове в асинхронных ситуациях. - person Servy; 16.12.2013
comment
@ Сервис имеет смысл. Так как я поправляюсь, использование Wait (timeOut) может помочь, верно? - person RredCat; 17.12.2013
comment
Затем вам нужно беспокоиться о достижении тайм-аута, когда операция на самом деле не выполняется, что очень плохо, а также о времени, потраченном на ожидание до истечения времени ожидания в тех случаях, когда он заходит в тупик (и в этом случае вы все еще продолжаю, когда это не сделано). Так что нет, это не решает проблемы. - person Servy; 17.12.2013
comment
@Servy Похоже, мне нужно реализовать CancellationToken для своего решения. - person RredCat; 17.12.2013
comment
@Servy Почему ты не мог бы немного объяснить? - person RredCat; 17.12.2013
comment
Я мог бы, или вы могли бы просто спросить об этом у Google, или, если на то пошло, просто посмотрите комментарии к ответам, которые более или менее дублируют ваши; в частности, тот, который набрал наибольшее количество голосов, поскольку у него есть [ссылка на] объяснение. - person Servy; 17.12.2013
comment
Ваш пример работает, потому что ваш метод GetCustomers () не является методом async с точки зрения C # 5.0 (без пары ключевых слов async-await), но просто метод возврата задачи. Вопрос касался метода async C # 5.0. Отметьте свой метод GetCustomers () как async, затем вызовите await Task.Run (), тогда вы, вероятно, попадете в тупик. - person sgnsajgon; 13.09.2014

Протестировано в .Net 4.6. Это также может избежать тупика.

Для асинхронного метода, возвращающего Task.

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

Для асинхронного метода, возвращающего Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

Изменить:

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

person Liang    schedule 24.10.2018
comment
Мой ответ спустя почти 8 лет :) Второй пример - вызовет взаимоблокировку во всех запланированных контекстах, которые в основном используются (консольное приложение / ядро ​​.NET / настольное приложение / ...). здесь у вас есть более подробный обзор того, о чем я говорю сейчас: medium.com/rubrikkgroup / - person Marek Woźniak; 14.02.2019
comment
Result идеально подходит для работы, если вам нужен синхронный вызов, и совершенно опасен в противном случае. В имени Result или в интеллектуальном значении Result нет ничего, что указывало бы на то, что это блокирующий вызов. Его действительно нужно переименовать. - person Zodman; 24.04.2020

В вашем коде ваше первое ожидание выполнения задачи, но вы еще не запустили ее, поэтому она ждет бесконечно. Попробуй это:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Изменить:

Вы говорите, что получаете исключение. Опубликуйте дополнительные сведения, включая трассировку стека.
Mono содержит следующий тестовый пример:

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

Проверьте, работает ли это для вас. Если это не так, хотя это очень маловероятно, у вас может быть странная сборка Async CTP. Если это сработает, вы можете проверить, что именно генерирует компилятор и чем Task создание экземпляра отличается от этого примера.

Изменить № 2:

Я проверил с помощью Reflector, что описанное вами исключение возникает, когда m_action равно null. Это немного странно, но я не эксперт по Async CTP. Как я уже сказал, вам следует декомпилировать свой код и посмотреть, как именно создается экземпляр Task, и почему его m_action равно null.

person Dan Abramov    schedule 23.02.2011
comment
Я скорректировал свой вопрос, чтобы сделать код, который я пытался, немного понятнее. RunSynchronously возвращает ошибку RunSynchronously may not be called on a task unbound to a delegate. Google не поможет, так как все результаты на китайском языке ... - person Rachel; 23.02.2011
comment
Думаю, разница в том, что я не создаю задачу, а затем пытаюсь ее запустить. Вместо этого задача создается асинхронным методом при использовании ключевого слова await. Исключение, опубликованное в моем предыдущем комментарии, является исключением, которое я получаю, хотя это одно из немногих, для которого я не могу найти причину или решение в Google. - person Rachel; 23.02.2011
comment
Ключевые слова async и async - не более чем синтаксический сахар. Компилятор генерирует код для создания Task<Customer> в GetCustomers(), поэтому я бы сначала посмотрел именно на него. Что касается исключения, вы отправили только сообщение об исключении, которое бесполезно без типа исключения и трассировки стека. Вызвать метод ToString() исключения и опубликовать вывод в вопросе. - person Dan Abramov; 23.02.2011
comment
@gaearon: я опубликовал сведения об исключении и трассировку стека в своем исходном вопросе. - person Rachel; 23.02.2011
comment
Мне также интересно, используете ли вы Task из .NET 4.0 (а не из какой-то предварительной версии) и стабильную версию Async CTP. - person Dan Abramov; 23.02.2011
comment
Я использую самую стабильную версию AsyncCTP за месяц или два назад и объект Task .Net 4.0. Я действительно нашел альтернативу, которая работает, хотя это не мой код, поэтому я все еще работаю, чтобы полностью понять его. - person Rachel; 23.02.2011
comment
@gaearon Я думаю, вы получили отрицательные голоса, потому что ваш пост не относится к вопросу. Речь идет о методах async-await, а не о простых методах, возвращающих Task. Более того, на мой взгляд, механизм async-await - это синтаксический сахар, но не такой тривиальный - есть продолжение, захват контекста, возобновление локального контекста, улучшенная обработка локальных исключений и многое другое. Затем вы не должны вызывать метод RunSynchronously для результата асинхронного метода, потому что по определению асинхронный метод должен возвращать задачу, которая в настоящее время, по крайней мере, запланирована, и более одного раза находится в состоянии выполнения. - person sgnsajgon; 15.09.2014

Почему бы не создать такой звонок:

Service.GetCustomers();

это не асинхронно.

person Daniel A. White    schedule 23.02.2011
comment
Это будет то, что я сделаю, если я не смогу заставить это работать ... создать версию Sync в дополнение к версии Async - person Rachel; 23.02.2011
comment
Что делать, если вы не можете писать свои собственные функции, поскольку используете библиотеку? - person Ferenc Dajka; 23.07.2020

используйте нижеприведенный фрагмент кода

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
person Mahesh    schedule 19.10.2016
comment
Task.WaitAll здесь ничего не добавляет. Почему бы просто не подождать? - person nawfal; 04.08.2020

ПРИМЕЧАНИЕ. Я считаю, что лучше всего не рекомендуется изменять характер действия, если оно асинхронное, лучше всего обрабатывать его как есть (полностью асинхронно). Таким образом, вы можете получить другие преимущества, такие как параллельная обработка / многопоточность и т. Д.

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

var customers = GetCustomersAsync().GetAwaiter().GetResult();
person Jaider    schedule 16.11.2020

Этот ответ предназначен для всех, кто использует WPF для .NET 4.5.

Если вы попытаетесь выполнить Task.Run() в потоке графического интерфейса пользователя, task.Wait() будет зависать на неопределенное время, если у вас нет ключевого слова async в определении вашей функции.

Этот метод расширения решает проблему, проверяя, находимся ли мы в потоке графического интерфейса пользователя, и, если да, запускаем задачу в потоке диспетчера WPF.

Этот класс может действовать как связующее звено между миром async / await и миром без async / await в ситуациях, когда это неизбежно, например, свойства MVVM или зависимости от других API, которые не используют async / await.

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}
person Contango    schedule 12.09.2014

Я думаю, что следующий вспомогательный метод также может решить проблему.

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

Может использоваться следующим образом:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
person donttellya    schedule 16.02.2016
comment
Это не синхронно. Вы создаете два потока и ждете первых результатов от другого. - person tmt; 18.05.2016
comment
и вообще, это очень плохая идея. - person Dan; 13.09.2016
comment
Я просто написал почти идентичный код (построчно то же самое), но вместо этого использовал SemaphoreSlim вместо события автоматического сброса. Хотел бы я увидеть это раньше. Я считаю, что этот подход предотвращает взаимоблокировки и поддерживает работу вашего асинхронного кода так же, как и в истинно асинхронных сценариях. Не совсем уверен, почему это плохая идея. Кажется намного чище, чем другие подходы, которые я видел выше. - person tmrog; 20.06.2017
comment
@DanPantry Я сейчас вижу несколько тупиковых ситуаций с этим подходом, который я не понимаю. Не могли бы вы объяснить, почему это плохая идея? - person tmrog; 20.06.2017
comment
Виноват. Я получил. это работает сейчас. Моя проблема заключалась в том, что я создавал задачу в основном потоке, а затем передавал ее в метод invoke async. Спасибо @donttellya, ваш код мне помог. - person tmrog; 20.06.2017

Простой вызов .Result; или .Wait() - это риск возникновения тупиковых ситуаций, о чем многие говорили в комментариях. Поскольку большинству из нас нравятся одинарные лайнеры, вы можете использовать их для .Net 4.5<

Получение значения с помощью асинхронного метода:

var result = Task.Run(() => asyncGetValue()).Result;

Синхронный вызов асинхронного метода

Task.Run(() => asyncMethod()).Wait();

Никаких проблем с взаимоблокировкой не возникнет из-за использования Task.Run.

Источник:

https://stackoverflow.com/a/32429753/3850405

person Ogglas    schedule 25.09.2017

Это работает для меня

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

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

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

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

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}
person Dan Nguyen    schedule 27.07.2018

Я обнаружил, что SpinWait отлично подходит для этого.

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

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

person Curtis    schedule 06.02.2019
comment
Это опрос (вращение), делегат будет брать поток из пула до 1000 раз в секунду. Он может не вернуть управление сразу после завершения задачи (до 10+ мс ошибка). Если завершится по таймауту, задача продолжит работу, что делает тайм-аут практически бесполезным. - person Sinatr; 20.05.2019
comment
На самом деле, я использую это повсюду в своем коде, и когда условие выполняется, SpinWaitSpinUntil () немедленно завершает работу. Таким образом, в зависимости от того, что наступит раньше, «условие выполнено» или тайм-аут, задача завершается. Он не запускается. - person Curtis; 22.05.2019

On wp8:

Заверните это:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

Назови это:

GetCustomersSynchronously();
person user2113284    schedule 26.02.2013
comment
Нет, это не сработает, потому что задача не ожидает делегата от конструктора (это делегат, а не задача ..) - person Rico Suter; 29.07.2013

Или вы можете просто пойти с:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

Чтобы это скомпилировать, убедитесь, что вы ссылаетесь на сборку расширения:

System.Net.Http.Formatting
person user2057962    schedule 08.03.2016

Попробуйте следующий код, который у меня работает:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
person gandhraj gayakwad    schedule 03.09.2015