usar programación programacion net mvc metodos funciones ejemplo ejecucion con como await asíncrona async asp asincrona c# async-await

programacion - programación asíncrona con c# pdf



¿Cómo llamar al método asíncrono del método síncrono en C#? (11)

Añadiendo una solución que finalmente resolvió mi problema, espero que ahorre el tiempo de alguien.

Primero lea un par de artículos de Stephen Cleary :

De las "dos mejores prácticas" en "No bloquear en código asíncrono", la primera no funcionó para mí y la segunda no fue aplicable (básicamente, si puedo usar await , ¡sí!).

Así que aquí está mi solución: envuelva la llamada dentro de Task.Run<>(async () => await FunctionAsync()); y espero que ya no haya ningún punto muerto .

Aquí está mi código:

public class LogReader { ILogger _logger; public LogReader(ILogger logger) { _logger = logger; } public LogEntity GetLog() { Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync()); return task.Result; } public async Task<LogEntity> GetLogAsync() { var result = await _logger.GetAsync(); // more code here... return result as LogEntity; } }

Tengo un método public async void Foo() que quiero llamar desde un método síncrono. Hasta ahora, todo lo que he visto de la documentación de MSDN está llamando a métodos asíncronos a través de métodos asíncronos, pero todo mi programa no está creado con métodos asíncronos.

¿Es esto posible?

Aquí hay un ejemplo de cómo llamar a estos métodos desde un método asíncrono: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

Ahora estoy buscando llamar a estos métodos asíncronos desde métodos de sincronización.


Esos métodos asíncronos de Windows tienen un pequeño método ingenioso llamado AsTask (). Puede usar esto para que el método se devuelva a sí mismo como una tarea, de modo que pueda llamar manualmente a Wait ().

Por ejemplo, en una aplicación de Windows Phone 8 Silverlight, puede hacer lo siguiente:

private void DeleteSynchronous(string path) { StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder; Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask(); t.Wait(); } private void FunctionThatNeedsToBeSynchronous() { // Do some work here // .... // Delete something in storage synchronously DeleteSynchronous("pathGoesHere"); // Do other work here // ..... }

¡Espero que esto ayude!


La programación asíncrona "crece" a través del código base. Se ha comparado con un virus zombie . La mejor solución es permitir que crezca, pero a veces eso no es posible.

He escrito algunos tipos en mi biblioteca Nito.AsyncEx para tratar con una base de código parcialmente asíncrono. Sin embargo, no hay una solución que funcione en todas las situaciones.

Solución A

Si tiene un método asíncrono simple que no necesita volver a sincronizarse con su contexto, puede usar la Task.WaitAndUnwrapException :

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

No desea utilizar Task.Wait o Task.Result porque envuelven las excepciones en AggregateException .

Esta solución solo es apropiada si MyAsyncMethod no se sincroniza con su contexto. En otras palabras, cada await en MyAsyncMethod debe terminar con ConfigureAwait(false) . Esto significa que no puede actualizar ningún elemento de la interfaz de usuario o acceder al contexto de solicitud ASP.NET.

Solución B

Si MyAsyncMethod no necesita volver a sincronizarse con su contexto, es posible que pueda usar AsyncContext.RunTask para proporcionar un contexto anidado:

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

* Actualización 14/04/2014: en versiones más recientes de la biblioteca, la API es la siguiente:

var result = AsyncContext.Run(MyAsyncMethod);

(Está bien usar Task.Result en este ejemplo porque RunTask propagará las excepciones de Task ).

La razón por la que puede necesitar AsyncContext.RunTask lugar de Task.WaitAndUnwrapException es debido a una posibilidad de interbloqueo bastante sutil que ocurre en WinForms / WPF / SL / ASP.NET:

  1. Un método síncrono llama a un método asíncrono, obteniendo una Task .
  2. El método síncrono hace una espera de bloqueo en la Task .
  3. El método async utiliza await sin ConfigureAwait .
  4. La Task no se puede completar en esta situación porque solo se completa cuando finaliza el método async ; el método async no se puede completar porque está intentando programar su continuación a SynchronizationContext , y WinForms / WPF / SL / ASP.NET no permitirá que se ejecute la continuación porque el método síncrono ya se está ejecutando en ese contexto.

Esta es una de las razones por las que es una buena idea usar lo máximo posible la ConfigureAwait(false) dentro de cada método async .

Solución C

AsyncContext.RunTask no funcionará en todos los escenarios. Por ejemplo, si el método async espera algo que requiera que se complete un evento de IU, entonces se bloqueará incluso con el contexto anidado. En ese caso, podría iniciar el método async en el grupo de subprocesos:

var task = TaskEx.RunEx(async () => await MyAsyncMethod()); var result = task.WaitAndUnwrapException();

Sin embargo, esta solución requiere un MyAsyncMethod que funcionará en el contexto del grupo de subprocesos. Por lo tanto, no puede actualizar los elementos de la IU ni acceder al contexto de solicitud ASP.NET. Y en ese caso, también puede agregar ConfigureAwait(false) a sus declaraciones de await y usar la solución A.


La respuesta más aceptada no es del todo correcta. Existe una solución que funciona en todas las situaciones: una bomba de mensajes ad-hoc (SynchronizationContext).

El subproceso que realiza la llamada se bloqueará como se espera, al mismo tiempo que garantiza que todas las continuaciones a las que se llama desde la función asíncrona no se bloqueen, ya que se calcularán en el contexto de sincronización de sincronización (mensaje pump) que se ejecuta en el subproceso de la llamada.

El código del ayudante de bomba de mensajes ad-hoc:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Microsoft.Threading { /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary> public static class AsyncPump { /// <summary>Runs the specified asynchronous method.</summary> /// <param name="asyncMethod">The asynchronous method to execute.</param> public static void Run(Action asyncMethod) { if (asyncMethod == null) throw new ArgumentNullException("asyncMethod"); var prevCtx = SynchronizationContext.Current; try { // Establish the new context var syncCtx = new SingleThreadSynchronizationContext(true); SynchronizationContext.SetSynchronizationContext(syncCtx); // Invoke the function syncCtx.OperationStarted(); asyncMethod(); syncCtx.OperationCompleted(); // Pump continuations and propagate any exceptions syncCtx.RunOnCurrentThread(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } /// <summary>Runs the specified asynchronous method.</summary> /// <param name="asyncMethod">The asynchronous method to execute.</param> public static void Run(Func<Task> asyncMethod) { if (asyncMethod == null) throw new ArgumentNullException("asyncMethod"); var prevCtx = SynchronizationContext.Current; try { // Establish the new context var syncCtx = new SingleThreadSynchronizationContext(false); SynchronizationContext.SetSynchronizationContext(syncCtx); // Invoke the function and alert the context to when it completes var t = asyncMethod(); if (t == null) throw new InvalidOperationException("No task provided."); t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default); // Pump continuations and propagate any exceptions syncCtx.RunOnCurrentThread(); t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } /// <summary>Runs the specified asynchronous method.</summary> /// <param name="asyncMethod">The asynchronous method to execute.</param> public static T Run<T>(Func<Task<T>> asyncMethod) { if (asyncMethod == null) throw new ArgumentNullException("asyncMethod"); var prevCtx = SynchronizationContext.Current; try { // Establish the new context var syncCtx = new SingleThreadSynchronizationContext(false); SynchronizationContext.SetSynchronizationContext(syncCtx); // Invoke the function and alert the context to when it completes var t = asyncMethod(); if (t == null) throw new InvalidOperationException("No task provided."); t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default); // Pump continuations and propagate any exceptions syncCtx.RunOnCurrentThread(); return t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } } /// <summary>Provides a SynchronizationContext that''s single-threaded.</summary> private sealed class SingleThreadSynchronizationContext : SynchronizationContext { /// <summary>The queue of work items.</summary> private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); /// <summary>The processing thread.</summary> private readonly Thread m_thread = Thread.CurrentThread; /// <summary>The number of outstanding operations.</summary> private int m_operationCount = 0; /// <summary>Whether to track operations m_operationCount.</summary> private readonly bool m_trackOperations; /// <summary>Initializes the context.</summary> /// <param name="trackOperations">Whether to track operation count.</param> internal SingleThreadSynchronizationContext(bool trackOperations) { m_trackOperations = trackOperations; } /// <summary>Dispatches an asynchronous message to the synchronization context.</summary> /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param> /// <param name="state">The object passed to the delegate.</param> public override void Post(SendOrPostCallback d, object state) { if (d == null) throw new ArgumentNullException("d"); m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); } /// <summary>Not supported.</summary> public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("Synchronously sending is not supported."); } /// <summary>Runs an loop to process all queued work items.</summary> public void RunOnCurrentThread() { foreach (var workItem in m_queue.GetConsumingEnumerable()) workItem.Key(workItem.Value); } /// <summary>Notifies the context that no more work will arrive.</summary> public void Complete() { m_queue.CompleteAdding(); } /// <summary>Invoked when an async operation is started.</summary> public override void OperationStarted() { if (m_trackOperations) Interlocked.Increment(ref m_operationCount); } /// <summary>Invoked when an async operation is completed.</summary> public override void OperationCompleted() { if (m_trackOperations && Interlocked.Decrement(ref m_operationCount) == 0) Complete(); } } } }

Uso:

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

Una descripción más detallada de la bomba asíncrona está disponible here .


Microsoft creó una clase AsyncHelper (interna) para ejecutar Async como Sync. La fuente se ve como:

internal static class AsyncHelper { private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); public static TResult RunSync<TResult>(Func<Task<TResult>> func) { return AsyncHelper._myTaskFactory .StartNew<Task<TResult>>(func) .Unwrap<TResult>() .GetAwaiter() .GetResult(); } public static void RunSync(Func<Task> func) { AsyncHelper._myTaskFactory .StartNew<Task>(func) .Unwrap() .GetAwaiter() .GetResult(); } }

Las clases base Microsoft.AspNet.Identity solo tienen métodos asíncronos y para llamarlos como Sync, hay clases con métodos de extensión que parecen (ejemplo de uso):

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

Para aquellos preocupados por los términos de licencia del código, aquí hay un enlace a un código muy similar (solo agrega soporte para la cultura en el hilo) que tiene comentarios que indican que es un MIT con licencia de Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


No estoy 100% seguro, pero creo que la técnica descrita en este blog debería funcionar en muchas circunstancias:

Por lo tanto, puede usar task.GetAwaiter().GetResult() si desea invocar directamente esta lógica de propagación.


Puede llamar a cualquier método asíncrono desde un código síncrono, es decir, hasta que necesite await , en cuyo caso también deben marcarse como async .

Como mucha gente está sugiriendo aquí, puede llamar a Wait () o Result en la tarea resultante en su método síncrono, pero luego termina con una llamada de bloqueo en ese método, que de alguna forma anula el propósito de async.

Si realmente no puede hacer que su método sea async y no quiere bloquear el método síncrono, entonces tendrá que usar un método de devolución de llamada pasándolo como parámetro al método ContinueWith en la tarea.


async Main ahora es parte de C # 7.2 y se puede habilitar en la configuración avanzada de compilación de proyectos.

Para C # <7.2, la forma correcta es:

static void Main(string[] args) { MainAsync().GetAwaiter().GetResult(); } static async Task MainAsync() { /*await stuff here*/ }


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


public async Task<string> StartMyTask() { await Foo() // code to execute once foo is done } static void Main() { var myTask = StartMyTask(); // call your method which will return control once it hits await // now you can continue executing code here string result = myTask.Result; // wait for the task to complete to continue // use result }

Usted lee la palabra clave ''esperar'' como "comience esta tarea de larga ejecución, luego devuelva el control al método de llamada". Una vez que se realiza la tarea de ejecución prolongada, se ejecuta el código posterior. El código después de la espera es similar a lo que solían ser los métodos de devolución de llamada. La gran diferencia es que el flujo lógico no se interrumpe, lo que facilita mucho la escritura y la lectura.


var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false); OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

O usa esto:

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