Tengo un async
metodo:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
Necesito llamar a este método desde un método sincrónico.
¿Cómo puedo hacer esto sin tener que duplicar el GenerateCodeAsync
método para que esto funcione sincrónicamente?
Actualizar
Sin embargo, no se encontró una solución razonable.
Sin embargo, veo que HttpClient
ya implementa este patrón
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Respuestas:
Puede acceder a la
Result
propiedad de la tarea, lo que hará que su hilo se bloquee hasta que el resultado esté disponible:Nota: en algunos casos, esto puede llevar a un punto muerto: su llamada para
Result
bloquear el hilo principal, evitando así que se ejecute el resto del código asincrónico. Tiene las siguientes opciones para asegurarse de que esto no suceda:.ConfigureAwait(false)
a su método de biblioteca oEjecute explícitamente su método asíncrono en un subproceso de grupo de subprocesos y espere a que termine:
¡Esto no significa que deba agregar sin pensar
.ConfigureAwait(false)
después de todas sus llamadas asíncronas! Para un análisis detallado de por qué y cuándo debe usar.ConfigureAwait(false)
, consulte la siguiente publicación de blog:fuente
result
arriesga a un punto muerto, ¿cuándo es seguro obtener el resultado? ¿Cada llamada asincrónica requiereTask.Run
oConfigureAwait(false)
?AspNetSynchronizationContext.Post
serializa las continuaciones asíncronas:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
para mantenerte seguro. O use algo comoWithNoContext
reducir la conmutación de subprocesos redundantes..Result
aún pueden llegar a un punto muerto si la persona que llama está en el grupo de subprocesos. Tomemos un escenario en el que el grupo de subprocesos sea de tamaño 32 y las 32 tareas se estén ejecutando yWait()/Result
esperando una 33a tarea aún por programar que quiera ejecutarse en uno de los subprocesos en espera.Debería obtener el camarero (
GetAwaiter()
) y finalizar la espera para completar la tarea asincrónica (GetResult()
).fuente
Task.GetAwaiter
: este método está destinado al uso del compilador en lugar de usarse en el código de la aplicación.Debería poder hacer esto usando delegados, expresión lambda
fuente
Es posible con
GenerateCodeAsync().Result
oGenerateCodeAsync().Wait()
, como sugiere la otra respuesta. Esto bloquearía el hilo actual hasta que seGenerateCodeAsync
haya completado.Sin embargo, su pregunta está etiquetada con asp.net, y también dejaste el comentario:
Mi punto es que no deberías estar bloqueando un método asincrónico en ASP.NET. Esto reducirá la escalabilidad de su aplicación web y puede crear un punto muerto (cuando se publica una
await
continuación en el interior ). El uso para descargar algo en un subproceso de grupo y luego bloquear dañará la escalabilidad aún más, ya que incurre en +1 subproceso adicional para procesar una solicitud HTTP dada.GenerateCodeAsync
AspNetSynchronizationContext
Task.Run(...).Result
ASP.NET tiene soporte incorporado para métodos asincrónicos, ya sea a través de controladores asincrónicos (en ASP.NET MVC y API web) o directamente a través
AsyncManager
yPageAsyncTask
en ASP.NET clásico. Deberías usarlo. Para más detalles, verifique esta respuesta .fuente
SaveChanges()
método deDbContext
, y aquí llamo a los métodos asíncronos, por lo que desafortunadamente el controlador asíncrono no me ayudará en esta situaciónSaveChangesAsync
ySaveChanges
, simplemente asegúrese de que no se los llame a ambos en el mismo proyecto ASP.NET..NET MVC
filtros admiten código asíncrono, por ejemploIAuthorizationFilter
, por lo que no puedo usarloasync
todo el tiempoMicrosoft Identity tiene métodos de extensión que llaman a métodos asíncronos sincrónicamente. Por ejemplo, hay un método GenerateUserIdentityAsync () e igual CreateIdentity ()
Si observa UserManagerExtensions.CreateIdentity () se verá así:
Ahora veamos qué hace AsyncHelper.RunSync
Entonces, este es su contenedor para el método asíncrono. Y, por favor, no lea los datos del Resultado: potencialmente bloqueará su código en ASP.
Hay otra forma, que es sospechosa para mí, pero también puedes considerarla
fuente
Para evitar puntos muertos siempre trato de usar
Task.Run()
cuando tengo que llamar a un método asíncrono sincrónicamente que @Heinzi menciona.Sin embargo, el método tiene que modificarse si el método asíncrono usa parámetros. Por ejemplo
Task.Run(GenerateCodeAsync("test")).Result
da el error:Esto podría llamarse así:
fuente
La mayoría de las respuestas en este hilo son complejas o resultarán en un punto muerto.
El siguiente método es simple y evitará un punto muerto porque estamos esperando que termine la tarea y solo entonces obteniendo su resultado:
Además, aquí hay una referencia al artículo de MSDN que habla exactamente de lo mismo: https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- en contexto principal /
fuente
Prefiero un enfoque sin bloqueo:
fuente
Bueno, estoy usando este enfoque:
fuente
La otra forma podría ser si desea esperar hasta que finalice la tarea:
fuente
EDITAR:
La tarea tiene el método Wait, Task.Wait (), que espera a que se resuelva la "promesa" y luego continúa, lo que la hace sincrónica. ejemplo:
fuente
Si tiene un método asincrónico llamado " RefreshList ", puede llamar a ese método asincrónico desde un método no asincrónico como se muestra a continuación.
fuente