c# español - ¿Cómo ejecutaría un método asíncrono de tarea<T>de forma sincrónica?




async await (21)

Si estoy leyendo su pregunta correctamente, el código que desea que la llamada síncrona a un método asíncrono se ejecute en un subproceso de distribuidor suspendido. Y desea bloquear ese hilo de forma síncrona hasta que se complete el método asíncrono.

Los métodos asíncronos en C # 5 se activan al cortar efectivamente el método en pedazos debajo del capó y al devolver una Task que puede rastrear la finalización general de todo el shabang. Sin embargo, la forma en que se ejecutan los métodos recortados puede depender del tipo de expresión que se pase al operador de await .

La mayoría de las veces, estará utilizando la await en una expresión de tipo Task . La implementación de la tarea del patrón de await es "inteligente", ya que difiere al SynchronizationContext , que básicamente hace que suceda lo siguiente:

  1. Si el subproceso que entra en la await está en un subproceso de bucle de mensajes de Dispatcher o WinForms, garantiza que los fragmentos del método asíncrono se produzcan como parte del procesamiento de la cola de mensajes.
  2. Si el subproceso que entra en la await está en un subproceso de conjunto de subprocesos, los fragmentos restantes del método asíncrono se producen en cualquier lugar del conjunto de subprocesos.

Es por eso que probablemente se encuentre con problemas (la implementación del método asíncrono está intentando ejecutar el resto en el Dispatcher) aunque esté suspendido.

.... retrocediendo! ....

Tengo que hacer la pregunta, ¿por qué intenta bloquear de forma síncrona en un método asíncrono? Hacer esto anularía el propósito de por qué el método quería llamarse de forma asíncrona. En general, cuando empiece a usar await en un Dispatcher o un método de UI, querrá convertir todo su flujo de UI en asíncrono. Por ejemplo, si su pila de llamadas fue algo como lo siguiente:

  1. [Arriba] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - Código WPF o WinForms
  6. [Message Loop] - WPF o WinForms Message Loop

Luego, una vez que el código se ha transformado para usar async, normalmente terminará con

  1. [Arriba] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - Código WPF o WinForms
  6. [Message Loop] - WPF o WinForms Message Loop

En realidad respondiendo

La clase AsyncHelpers anterior realmente funciona porque se comporta como un bucle de mensaje anidado, pero instala su propio mecanismo paralelo en el Dispatcher en lugar de intentar ejecutarlo en el Dispatcher. Esa es una solución para su problema.

Otra solución es ejecutar el método asíncrono en un subproceso de subprocesos y luego esperar a que se complete. Hacerlo es fácil, puede hacerlo con el siguiente fragmento de código:

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

La API final será Task.Run (...), pero con el CTP necesitará los sufijos Ex ( explicación aquí ).

Estoy aprendiendo sobre async / await, y me encontré con una situación en la que necesito llamar un método asíncrono de forma síncrona. ¿Cómo puedo hacer eso?

Método asíncrono:

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

Uso normal:

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

He intentado usar lo siguiente:

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

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

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

También intenté una sugerencia desde here , sin embargo, no funciona cuando el operador está en estado suspendido.

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

Aquí está la excepción y el seguimiento de la pila de llamar a RunSynchronously :

System.InvalidOperationException

Mensaje : RunSynchronously no se puede llamar en una tarea no vinculada a un delegado.

InnerException : null

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

Solo una pequeña nota - este enfoque:

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

Trabaja para WinRT.

Dejame explicar:

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

Además, este enfoque funciona solo para las soluciones de la Tienda Windows.

Nota: esta forma no es segura para subprocesos si llama a su método dentro de otro método asíncrono (según los comentarios de @Servy)


En wp8:

Envolverlo:

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

Llámalo:

GetCustomersSynchronously();

Esto me funciona

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

Encontré este código en el componente Microsoft.AspNet.Identity.Core, y funciona.

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

Simplemente llamar .Result;o .Wait()es un riesgo de interbloqueos como muchos han dicho en los comentarios. Como a la mayoría de nosotros nos gustan los oneliners, puedes usarlos para.Net 4.5<

Adquiriendo un valor a través de un método asíncrono:

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

Llamando sincrónicamente a un método asíncrono.

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

No se producirán problemas de interbloqueo debido al uso de Task.Run.

Fuente:

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


¿Por qué no crear una llamada como:

Service.GetCustomers();

Eso no es asíncrono.


Esta respuesta está diseñada para cualquier persona que use WPF para .NET 4.5.

Si intenta ejecutar Task.Run()en el subproceso de la GUI, task.Wait()se bloqueará indefinidamente, si no tiene la asyncpalabra clave en la definición de su función.

Este método de extensión resuelve el problema al verificar si estamos en el subproceso de la GUI y, de ser así, ejecutar la tarea en el subproceso del distribuidor de WPF.

Esta clase puede actuar como el pegamento entre el mundo async / await y el mundo no async / await, en situaciones donde es inevitable, como las propiedades MVVM o las dependencias de otras API que no usan 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();
        }
    }
}

Estoy aprendiendo sobre async / await, y me encontré con una situación en la que necesito llamar un método asíncrono de forma síncrona. ¿Cómo puedo hacer eso?

La mejor respuesta es que no , con los detalles que dependen de lo que es la "situación".

¿Es una propiedad getter / setter? En la mayoría de los casos, es mejor tener métodos asíncronos que "propiedades asíncronas". (Para más información, vea mi publicación de blog sobre propiedades asíncronas ).

¿Es esta una aplicación MVVM y desea realizar un enlace de datos asíncrono? Luego use algo como mi NotifyTask , como se describe en mi artículo de MSDN sobre enlace de datos asíncrono .

¿Es un constructor? Entonces probablemente querrás considerar un método de fábrica asíncrono. (Para más información, vea mi blog en constructores asíncronos ).

Casi siempre hay una mejor respuesta que hacer sync-over-async.

Si no es posible para su situación (y lo sabe haciendo una pregunta aquí que describe la situación ), le recomiendo que solo utilice un código síncrono. Async todo el camino es mejor; Sincronizar todo el camino es el segundo mejor. No se recomienda la sincronización sobre asíncrono.

Sin embargo, hay un puñado de situaciones donde es necesario sincronizar sobre asíncrono. Específicamente, el código de llamada lo restringe para que tenga que estar sincronizado (y no tiene absolutamente ninguna manera de repensar o reestructurar su código para permitir la asincronía), y debe llamar al código asíncrono. Esta es una situación muy rara, pero surge de vez en cuando.

En ese caso, necesitaría usar uno de los trucos descritos en mi artículo sobre el desarrollo async Brownfield , específicamente:

  • Bloqueo (por ejemplo, GetAwaiter().GetResult() ). Tenga en cuenta que esto puede causar puntos muertos (como describo en mi blog).
  • Ejecutar el código en un subproceso de grupo de subprocesos (por ejemplo, Task.Run(..).GetAwaiter().GetResult() ). Tenga en cuenta que esto solo funcionará si el código asíncrono se puede ejecutar en un subproceso de grupo de subprocesos (es decir, no depende de un contexto de UI o ASP.NET).
  • Bucles de mensajes anidados. Tenga en cuenta que esto solo funcionará si el código asíncrono solo asume un contexto de un solo hilo, no un tipo de contexto específico (muchos UI y código ASP.NET esperan un contexto específico).

Los bucles de mensajes anidados son los más peligrosos de todos los hacks, ya que causan una re-entrancy . La re-entrada es extremadamente difícil de razonar, y (IMO) es la causa de la mayoría de los errores de aplicaciones en Windows. En particular, si está en el subproceso de la interfaz de usuario y bloquea en una cola de trabajo (a la espera de que se complete el trabajo asíncrono), entonces el CLR realiza un bombeo de mensajes por usted, en realidad manejará algunos mensajes de Win32 desde su codigo Ah, y no tienes idea de qué mensajes, cuando Chris Brumme dice "¿No sería genial saber exactamente qué se bombeará? Desafortunadamente, bombear es un arte negro que está más allá de la comprensión mortal". , entonces realmente no tenemos ninguna esperanza de saber.

Entonces, cuando bloqueas así en un subproceso de la interfaz de usuario, estás pidiendo problemas. Otra cita del mismo artículo: "De vez en cuando, los clientes dentro o fuera de la empresa descubren que estamos bombeando mensajes durante el bloqueo administrado en un STA [subproceso de la interfaz de usuario]. Esta es una preocupación legítima, porque saben que es muy difícil escribir código que sea robusto ante la reentrada ".

Sí lo es. Muy difícil de escribir código que sea robusto ante la reentrada. Y los bucles de mensajes anidados te obligan a escribir código que sea robusto frente a la reentrada. Esta es la razón por la que la respuesta aceptada (y la más votada) para esta pregunta es extremadamente peligrosa en la práctica.

Si está completamente fuera de todas las demás opciones: no puede rediseñar su código, no puede reestructurarlo para que sea asíncrono, se ve obligado a sincronizar el código de llamada inalterable, no puede cambiar el código de abajo para que se sincronice - no puede bloquear - no puede ejecutar el código asíncrono en un subproceso separado - entonces y solo entonces debería considerar abrazar la reentrada.

Si se encuentra en este rincón, le recomendaría usar algo como Dispatcher.PushFrame para las aplicaciones WPF , hacer un bucle con las Application.DoEvents para WinForm, y para el caso general, mi propio AsyncContext.Run .


Creo que el siguiente método de ayuda también podría resolver el problema.

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

Se puede utilizar de la siguiente manera:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

Ten en cuenta que esta respuesta tiene tres años. Lo escribí basado principalmente en una experiencia con .Net 4.0, y muy poco con 4.5, especialmente con async-await . En general, es una buena solución simple, pero a veces rompe cosas. Por favor lea la discusión en los comentarios.

.Net 4.5

Solo usa esto:

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

Ver: TaskAwaiter , Task.Result , Task.RunSynchronously

.Net 4.0

Utilizar esta:

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

x.AsyncWaitHandle.WaitOne();

...o esto:

task.Start();
task.Wait();

use el código siguiente

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

La forma más sencilla que he encontrado de ejecutar la tarea de forma síncrona y sin bloquear el subproceso de la interfaz de usuario es usar Ejecutar de forma síncrona () como:

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

En mi caso, tengo un evento que se dispara cuando ocurre algo. No sé cuántas veces ocurrirá. Por lo tanto, uso el código anterior en mi evento, así que cada vez que se dispara, crea una tarea. Las tareas se ejecutan de forma síncrona y funciona muy bien para mí. Me sorprendió que me haya costado tanto descubrirlo, considerando lo simple que es. Por lo general, las recomendaciones son mucho más complejas y propensas a errores. Esto fue simple y limpio.


Intenta seguir el código que me funciona:

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

Esto esta funcionando bien para mi

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

Aquí hay una solución que encontré que funciona para todos los casos (incluidos los despachadores suspendidos). No es mi código y todavía estoy trabajando para entenderlo completamente, pero funciona.

Se puede llamar usando:

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

El código es de here

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

Probado en .Net 4.6. También puede evitar el interbloqueo.

Para el retorno de un método aysnc Task.

Task DoSomeWork()

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

Para el retorno del método asíncrono. Task<T>

Task<T> GetSomeValue()

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

En su código, su primera espera para que se ejecute la tarea, pero no la ha iniciado, por lo que espera indefinidamente. Prueba esto:

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

Editar:

Usted dice que obtiene una excepción. Por favor, publicar más detalles, incluyendo el seguimiento de la pila.
Mono contains el siguiente caso de prueba:

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

        Assert.AreEqual (1, val);
}

Compruebe si esto funciona para usted. Si no es así, aunque es muy poco probable, es posible que tenga una compilación impar de CTP asíncrono. Si funciona, es posible que desee examinar qué genera exactamente el compilador y cómo Task creación de instancias de Task es diferente de esta muestra.

Edición # 2:

Verifiqué con Reflector que la excepción que describiste ocurre cuando m_actiones null. Esto es un poco extraño, pero no soy un experto en CTP asíncrono. Como dije, debes descompilar tu código y ver cómo Taskse está instanciando exactamente cómo m_actiones null.

PD: ¿Cuál es el trato con los downvotes ocasionales? ¿Cuidado para elaborar?


O simplemente puedes ir con:

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

Para compilar esto, asegúrese de hacer referencia al ensamble de extensión:

System.Net.Http.Formatting

Lo he enfrentado varias veces, principalmente en pruebas de unidad o en el desarrollo de un servicio de Windows. Actualmente siempre uso esta característica:

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

Es simple, fácil y no tuve problemas.


No lo he usado con éxito antes, pero aquí está mi ir:

Un Lookup<TKey, TElement> se comportaría más o menos como un índice de base de datos (relacional) en una tabla sin una restricción única. Úselo en los mismos lugares donde usaría el otro.





c# asynchronous c#-5.0 async-await