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

Я изучаю 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:

.Исключение 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.CustomControlsControlsMyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:Documents and Settings...MyApplication.CustomControlsControlsMyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:Documents and Settings...MyApplication.CustomControlsControlsMyCustomControl.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.CustomControlsControlsMyCustomControl.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...MyApplicationobjDebugApp.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()

23 ответов


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

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

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, задач.Результат, задач.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), а не только задачи.


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

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

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, и вы хотите сделать асинхронную привязку данных? Тогда используйте что-то вроде my NotifyTask, как описано в моем статья MSDN об асинхронной привязке данных.

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

.... назад! ....

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

  1. [Top] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF или WinForms код
  6. [Цикл Обработки Сообщений] - WPF или WinForms Цикл Обработки Сообщений

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

  1. [Top] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF или WinForms код
  6. [Цикл Обработки Сообщений] - WPF или WinForms Цикл Обработки Сообщений

На Самом Деле Отвечает

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

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

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

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


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

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

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


Я сталкивался с этим несколько раз, в основном в модульном тестировании или в разработке службы 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.сеть САШ.Тождественность.Основной компонент, и он работает.

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

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

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)


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

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

Edit:

вы говорите, что вы получаете исключение. Пожалуйста, напишите более подробную информацию, включая трассировку стека.
Моно содержит следующий тест:

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

        Assert.AreEqual (1, val);
}

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

Edit #2:

Я проверил с рефлектором, что исключение, которое вы описали, происходит, когда m_action is null. Это немного странно, но я не эксперт по асинхронному CTP. Как я уже сказал, Вы должны декомпилировать свой код и посмотреть, как именно Task создается какой-либо экземпляр его m_action is null.


P. S. А что там с редкими минусами? Не хотите уточнить?


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

Service.GetCustomers();

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


используйте ниже код snip

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

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

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

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

этот класс может действовать как клей между миром 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();
        }
    }
}

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

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

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

Syncronously вызове метода async

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

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

источник:

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


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

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


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

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

на wp8:

оберните его:

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

называем это:

GetCustomersSynchronously();

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

или вы могли бы просто пойти с:

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

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

System.Net.Http.Formatting

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

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