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

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

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

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: ноль

Источник: 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()

29 ответов

Решение

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

Это можно вызвать используя:

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;
        }
    }
}

Имейте в виду, что этому ответу три года. Я написал это на основе опыта работы с.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();

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

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

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

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

  • это не глотает исключения (как Wait)
  • это не обернет любые исключения, брошенные в AggregateException (лайк Result)
  • работает для обоих Task а также Task<T> ( попробуйте сами!)

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


редактировать: обратите внимание, что это возможно для этого подхода (или с использованием .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()

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

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; 

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

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

Это свойство getter/setter? В большинстве случаев лучше иметь асинхронные методы, чем "асинхронные свойства". (Для получения дополнительной информации см. Мой пост в блоге об асинхронных свойствах).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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 ( объяснение здесь).

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

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;
    }
}

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

Для возврата метода aysnc Task,

Task DoSomeWork()

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

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

Task<T> GetSomeValue()

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

Я сталкивался с этим несколько раз, в основном в модульном тестировании или в разработке службы 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");

Это просто, легко, и у меня не было проблем.

Я нашел этот код в компоненте 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();
}

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

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

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

Просто небольшая заметка - этот подход:

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).

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

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

var customers = GetCustomersAsync().GetAwaiter().GetResult();

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

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,


PS Какое дело со случайными понижениями? Хотите разработать?

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

Service.GetCustomers();

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

Используйте приведенный ниже код

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

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

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

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

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

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

Из-за использования Task.Run,

Источник:

/questions/33975157/await-rabotaet-no-vyizyivaet-zadachu-rezultat-zavisaet-blokiruetsya/33975168#33975168

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

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

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

Этот класс может служить связующим звеном между миром async/await и миром non-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();
        }
    }
}

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

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();
        }
    }
}

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

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);

Вы можете использовать CoRoutines. Смотрите Caliburn.Micro реализации. У меня есть пользовательская реализация здесь.

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

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

Кажется, это работает для меня:

      Task.Run(GetCustomers).Wait();

Выполнение асинхронной задачи из синхронного кода создает несколько довольно больших проблем в обычном .NET:

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

Я действительно думаю, что у нас должна быть эта функция взаимодействия в .NET BCL. Между тем, вы можете использовать класс изGapotchenko.FX.ThreadingNuGet-пакет. Он обеспечивает бесшовное взаимодействие между синхронными и асинхронными моделями выполнения кода:

      using Gapotchenko.FX.Threading.Tasks;
using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        TaskBridge.Execute(RunAsync);
    }

    static async Task RunAsync()
    {
        await Console.Out.WriteLineAsync("Hello, Async World!");
    }
}

Пакет NuGet с классом доступен здесь .


Модели отмены

TaskBridgeобеспечивает автоматическую совместимость между различными моделями отмены.

Давайте вызовем отменяемый асинхронный метод из синхронного потока, который можно прервать с помощью метода:

      void SyncMethod() // can be canceled by Thread.Abort()
{
    // Executes an async task that is gracefully canceled via cancellation
    // token when current thread is being aborted or interrupted.
    TaskBridge.Execute(DoJobAsync); // <-- TaskBridge DOES THE MAGIC
}

async Task DoJobAsync(CancellationToken ct)
{
    …
    // Gracefully handles cancellation opportunities.
    ct.ThrowIfCancellationRequested();
    …
}

Теперь давайте рассмотрим противоположный сценарий, в котором отменяемая асинхронная задача вызывает прерываемый синхронный код:

      async Task DoJobAsync(CancellationToken ct) // can be canceled by a specified cancellation token
{
    // Executes a synchronous method that is thread-aborted when
    // a specified cancellation token is being canceled.
    await TaskBridge.ExecuteAsync(SyncMethod, ct); // <-- TaskBridge DOES THE MAGIC
}

void SyncMethod()
{
    …
}

Приведенный выше код демонстрирует простой однострочный код для полной совместимости между двумя моделями исполнения.

Примечание. Более новые версии .NET, начиная с .NET Core, не поддерживаютThread.Abort(). Это не имеет большого значения, потому что вместо этого вы можете просто передать токен отмены в отменяемые методы.

Я обнаружил, что 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(). Это также позволяет вам указать тайм-аут, чтобы вы не застряли навсегда на случай, если задача никогда не завершится.

Я знаю, что это старый вопрос, но я хотел бы поделиться своим решением, возможно, не лучшим, но выполняет свою работу:

      //Declare an Event Handler:
private event EventHandler OnThemeApply;

//..somewhere in your code..

//we 'hear' the event handler
this.OnThemeApply += (object _Sender, EventArgs _E) =>
{
  //Reaches here After the Async method had finished
  
  this.OnThemeApply = null;
};
MyAsycMethod();

private void MyAsycMethod()
{
   var t = System.Threading.Tasks.Task.Factory.StartNew(delegate
   {
      //Do something here

      Invoke((MethodInvoker)(() =>
      {
         if(this.OnThemeApply != null) this.OnThemeApply(null, null); //<- Calls the Event
      }));
   });
}

На wp8:

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

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

Назови это:

GetCustomersSynchronously();

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

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

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

System.Net.Http.Formatting
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }

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

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);
    }
}
Другие вопросы по тегам