c# - sincronico - Llamando al método asincrónico sincrónicamente
sincronicas (5)
Necesito llamar a este método desde un método sincrónico.
Es posible con GenerateCodeAsync().Result
o GenerateCodeAsync().Wait()
, como sugiere la otra respuesta. Esto bloquearía el hilo actual hasta que se complete GenerateCodeAsync
.
Sin embargo, su pregunta está etiquetada con asp.net , y también dejó el comentario:
Esperaba una solución más simple, pensando que asp.net manejó esto mucho más fácil que escribir tantas líneas de código
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 await
una continuación dentro de GenerateCodeAsync
en AspNetSynchronizationContext
). El uso de Task.Run(...).Result
de descargar algo a un subproceso del grupo y luego bloquear afectará aún más la escalabilidad, ya que incurre en +1 subproceso más para procesar una solicitud HTTP determinada.
ASP.NET tiene soporte integrado para métodos asincrónicos, ya sea a través de controladores asíncronos (en ASP.NET MVC y API web) o directamente a través de AsyncManager
y PageAsyncTask
en ASP.NET clásico. Deberías usarlo Para más detalles, verifique esta respuesta .
Tengo un método async
:
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 método GenerateCodeAsync
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;
}
Debe obtener el awaiter ( GetAwaiter()
) y finalizar la espera para completar la tarea asincrónica ( GetResult()
).
string code = GenerateCodeAsync().GetAwaiter().GetResult();
Debería poder hacer esto usando delegados, expresión lambda
private void button2_Click(object sender, EventArgs e)
{
label1.Text = "waiting....";
Task<string> sCode = Task.Run(async () =>
{
string msg =await GenerateCodeAsync();
return msg;
});
label1.Text += sCode.Result;
}
private Task<string> GenerateCodeAsync()
{
return Task.Run<string>(() => GenerateCode());
}
private string GenerateCode()
{
Thread.Sleep(2000);
return "I m back" ;
}
Microsoft Identity tiene métodos de extensión que llaman a los métodos asincrónicos sincrónicamente. Por ejemplo, hay un método GenerateUserIdentityAsync () e igual CreateIdentity ()
Si miras UserManagerExtensions.CreateIdentity () se verá así:
public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
string authenticationType)
where TKey : IEquatable<TKey>
where TUser : class, IUser<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
}
Ahora veamos qué hace AsyncHelper.RunSync
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
var cultureUi = CultureInfo.CurrentUICulture;
var culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew(() =>
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().GetAwaiter().GetResult();
}
Entonces, este es su contenedor para el método asíncrono. Y, por favor, no lea los datos de los resultados: potencialmente bloqueará su código en ASP.
Hay otra forma, lo cual es sospechoso para mí, pero también puedes considerarlo
Result r = null;
YourAsyncMethod()
.ContinueWith(t =>
{
r = t.Result;
})
.Wait();
Puede acceder a la propiedad Result
de la tarea, lo que hará que su hilo se bloquee hasta que el resultado esté disponible:
string code = GenerateCodeAsync().Result;
Nota: En algunos casos, esto puede llevar a un interbloqueo: su llamada a Result
bloquea el hilo principal, impidiendo así que se ejecute el resto del código asincrónico. Tiene las siguientes opciones para asegurarse de que esto no suceda:
- Agregue
.ConfigureAwait(false)
a su método de biblioteca o Ejecute explícitamente su método async en un hilo del grupo de subprocesos y espere a que finalice:
string code = Task.Run(GenerateCodeAsync).Result;