usar understanding puede funciones ejemplos ejemplo await async asincronos c# asp.net multithreading asynchronous task-parallel-library

understanding - task c# ejemplo



Llamar a métodos asíncronos desde código no asíncrono (2)

Así que me queda la mejor forma de llamar a los métodos asíncronos de forma síncrona.

Primero, esto está bien. Estoy afirmando esto porque es común en Stack Overflow señalar esto como una acción del diablo como una declaración general sin tener en cuenta el caso concreto.

No es necesario estar asíncrono por completo para la corrección . Bloquear algo asíncrono para sincronizarlo tiene un costo de rendimiento que puede ser importante o totalmente irrelevante. Depende del caso concreto.

Los puntos muertos provienen de dos subprocesos que intentan ingresar al mismo contexto de sincronización de un solo subproceso al mismo tiempo. Cualquier técnica que evite esto evita de manera confiable los bloqueos causados ​​por el bloqueo.

Aquí, todas sus llamadas a .ConfigureAwait(false) tienen sentido porque no está esperando.

RunSynchronously no es válido para usar porque no todas las tareas se pueden procesar de esa manera.

.GetAwaiter().GetResult() es diferente de Result/Wait() en que imita el comportamiento de propagación de excepción de await . Debes decidir si quieres eso o no. (Así que investigue cuál es ese comportamiento; no es necesario repetirlo aquí).

Además de eso, todos estos enfoques tienen un rendimiento similar. Asignarán un evento del sistema operativo de una forma u otra y lo bloquearán. Esa es la parte costosa. No sé qué enfoque es absolutamente más barato.

Personalmente, me gusta Task.Run(() => DoSomethingAsync()).Wait(); patrón porque evita los puntos muertos categóricamente, es simple y no oculta algunas excepciones que GetResult() podría ocultar. Pero también puede usar GetResult() con esto.

Estoy en el proceso de actualizar una biblioteca que tiene una superficie API que se creó en .NET 3.5. Como resultado, todos los métodos son sincrónicos. No puedo cambiar la API (es decir, convertir los valores de retorno a Tarea) porque eso requeriría que todas las personas que llaman cambien. Así que me queda la mejor forma de llamar a los métodos asíncronos de forma síncrona. Esto está en el contexto de las aplicaciones de consola ASP.NET 4, ASP.NET Core y .NET / .NET Core.

Es posible que no haya sido lo suficientemente claro: la situación es que tengo un código existente que no es asíncrono, y quiero usar nuevas bibliotecas como System.Net.Http y el SDK de AWS que solo admiten métodos asíncronos. Por lo tanto, necesito cerrar la brecha y poder tener un código que se pueda llamar sincrónicamente, pero que luego pueda llamar a métodos asíncronos en otro lugar.

He leído mucho, y hay varias veces que esto se ha preguntado y respondido.

Llamar al método asincrónico desde un método no asincrónico

Sincrónicamente esperando una operación asincrónica, y por qué Wait () congela el programa aquí

Llamar a un método asíncrono desde un método síncrono

¿Cómo ejecutaría un método asíncrono de Tarea <T> sincrónicamente?

Llamar al método asíncrono sincrónicamente

¿Cómo llamar al método asincrónico desde el método sincrónico en C #?

¡El problema es que la mayoría de las respuestas son diferentes! El enfoque más común que he visto es el uso .Resultado, pero esto puede llegar a un punto muerto. He intentado todo lo siguiente, y funcionan, pero no estoy seguro de cuál es el mejor enfoque para evitar puntos muertos, tener un buen rendimiento y jugar bien con el tiempo de ejecución (en términos de respetar los programadores de tareas, las opciones de creación de tareas, etc. ) ¿Hay una respuesta definitiva? ¿Cuál es el mejor enfoque?

private static T taskSyncRunner<T>(Func<Task<T>> task) { T result; // approach 1 result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult(); // approach 2 result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult(); // approach 3 result = task().ConfigureAwait(false).GetAwaiter().GetResult(); // approach 4 result = Task.Run(task).Result; // approach 5 result = Task.Run(task).GetAwaiter().GetResult(); // approach 6 var t = task(); t.RunSynchronously(); result = t.Result; // approach 7 var t1 = task(); Task.WaitAll(t1); result = t1.Result; // approach 8? return result; }


Estoy en el proceso de actualizar una biblioteca que tiene una superficie API que se creó en .NET 3.5. Como resultado, todos los métodos son sincrónicos. No puedo cambiar la API (es decir, convertir los valores de retorno a Tarea) porque eso requeriría que todas las personas que llaman cambien. Así que me queda la mejor forma de llamar a los métodos asíncronos de forma síncrona.

No existe una "mejor" forma universal de realizar el antipatrón sync-over-async. Solo una variedad de hacks que tienen sus propios inconvenientes.

Lo que recomiendo es que mantenga las API síncronas antiguas y luego introduzca API asíncronas junto a ellas. Puede hacerlo utilizando el "truco de argumento booleano" como se describe en mi artículo de MSDN sobre Brownfield Async .

Primero, una breve explicación de los problemas con cada enfoque en su ejemplo:

  1. ConfigureAwait solo tiene sentido cuando hay una await ; de lo contrario, no hace nada.
  2. Result envolverá las excepciones en una AggregateException ; si debe bloquear, use GetAwaiter().GetResult() lugar.
  3. Task.Run ejecutará su código en un subproceso de grupo de subprocesos (obviamente). Esto está bien solo si el código puede ejecutarse en un subproceso de grupo de subprocesos.
  4. RunSynchronously es una API avanzada que se usa en situaciones extremadamente raras cuando se realiza un paralelismo dinámico basado en tareas. No estás en ese escenario en absoluto.
  5. Task.WaitAll con una sola tarea es lo mismo que solo Wait() .
  6. async () => await x es solo una forma menos eficiente de decir () => x .
  7. Bloquear una tarea iniciada desde el hilo actual puede causar puntos muertos .

Aquí está el desglose:

// Problems (1), (3), (6) result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult(); // Problems (1), (3) result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult(); // Problems (1), (7) result = task().ConfigureAwait(false).GetAwaiter().GetResult(); // Problems (2), (3) result = Task.Run(task).Result; // Problems (3) result = Task.Run(task).GetAwaiter().GetResult(); // Problems (2), (4) var t = task(); t.RunSynchronously(); result = t.Result; // Problems (2), (5) var t1 = task(); Task.WaitAll(t1); result = t1.Result;

En lugar de cualquiera de estos enfoques, dado que tiene un código síncrono existente que funciona , debe usarlo junto con el código asincrónico natural más nuevo. Por ejemplo, si su código existente usaba WebClient :

public string Get() { using (var client = new WebClient()) return client.DownloadString(...); }

y quieres agregar una API asíncrona, entonces lo haría así:

private async Task<string> GetCoreAsync(bool sync) { using (var client = new WebClient()) { return sync ? client.DownloadString(...) : await client.DownloadStringTaskAsync(...); } } public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult(); public Task<string> GetAsync() => GetCoreAsync(sync: false);

o, si debe usar HttpClient por algún motivo:

private string GetCoreSync() { using (var client = new WebClient()) return client.DownloadString(...); } private static HttpClient HttpClient { get; } = ...; private async Task<string> GetCoreAsync(bool sync) { return sync ? GetCoreSync() : await HttpClient.GetString(...); } public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult(); public Task<string> GetAsync() => GetCoreAsync(sync: false);

Con este enfoque, su lógica iría a los métodos Core , que pueden ejecutarse sincrónicamente o asincrónicamente (según lo determine el parámetro de sync ). Si la sync es true , entonces los métodos principales deben devolver una tarea ya completada. Para la implementación, use API síncronas para ejecutar sincrónicamente y use API asincrónicas para ejecutar de forma asincrónica.

Finalmente, recomiendo desaprobar las API síncronas.