method - httpclient.getasync example c#
¿Cómo creo un método naturalmente asíncrono cuando las llamadas internas no son naturalmente asincrónicas? (2)
En este escenario, el sistema A necesita enviar un mensaje al sistema B. El siguiente código muestra una muestra de cómo se logró esto:
public interface IExecutionStrategy
{
Task<Result> ExecuteMessage(Message message);
}
public class WcfExecutionStrategy
{
public async Task<Result> ExecuteMessage(Message message)
{
using (var client = new Client())
{
return await client.RunMessageOnServer(message);
}
}
}
public class MessageExecutor
{
private readonly IExecutionStrategy _strategy;
public MessageExecutor(IExecutionStrategy strategy)
{
_strategy = strategy;
}
public Task<Result> ExecuteMessage(Message msg)
{
// ....
// Do some common checks and code here
// ....
var result = await _strategy.ExecuteMessage(msg);
// ....
// Do some common cleanup and logging here
// .....
return result;
}
}
Por razones fuera del alcance de esta pregunta, decidimos cambiar de Wcf a usar secuencias HTTP sin formato, pero necesitábamos ambas cosas juntas para recopilar métricas y probarlas. Así que creé una nueva implementación de IExecutionStrategy
para manejar esto:
public class HttpclientExecutionStrategy
{
public async Task<Result> ExecuteMessage(Message message)
{
var request = CreateWebRequestmessage
var responseStream = await Task.Run(() =>
{
var webResponse = (HttpWebResponse)webRequest.GetResponse();
return webResponse.GetResponseStream();
}
return MessageStreamer.ReadResultFromStream(responseStream);
}
}
Esencialmente, la única forma en que podía hacer que esto fuera asincrónico era envolverlo en una Task.Run()
para que la solicitud web no Task.Run()
ningún bloqueo. ( Nota : debido a los requisitos únicos de manipulación de flujo tanto para enviar como para recibir, no es sencillo implementar esto en HttpClient
, e incluso si es posible, este hecho está fuera del alcance de esta pregunta).
Pensamos que esto estaba bien hasta que leímos las publicaciones de blog de Stephen Cleary sobre cómo Task.Run()
es malo, tanto en el código de la biblioteca como en las aplicaciones de Asp.Net. Esto tiene sentido para mi.
Lo que no tiene sentido es cómo implementar realmente una llamada asíncrona natural si la biblioteca de terceros no admite un movimiento asincrónico. Por ejemplo, si tuviera que usar HttpClient.GetStreamAsync()
¿qué hace eso que lo hace mejor para operaciones asincrónicas que Task.Run(() => HttpClient.GetStream())
, y hay alguna manera de remediar esto para no -asincronizar bibliotecas de terceros?
Respuesta corta
Use async-await
si la operación es realmente asincrónica. Si no es así, entonces no pretendas que sí.
Respuesta larga
Las operaciones asíncronas tienen solo 2 beneficios reales: escalabilidad y descarga . La escalabilidad es principalmente para el lado del servidor, mientras que la descarga se usa para hilos "especiales" (principalmente hilos de GUI
).
Si depende de una biblioteca síncrona y no hay nada que pueda hacer, hacer que sea asincrónico mediante la Task.Run
no mejora en absoluto la escalabilidad (y de hecho puede dificultarlo), y solo le queda la descarga. async
reduce el uso de recursos solo cuando la operación es inherentemente asincrónica, cuando no hay subprocesos en la parte asíncrona real (red, disco, etc.)
Si está desarrollando una aplicación de cliente enriquecido, siga adelante y use "sync over async" con async-await
. Pero para cualquier otro tipo de aplicación, no hay ninguna razón para usar async-await
, y recomendaría no hacerlo.
HttpClient.GetStreamAsync
es un método asíncrono puro, lo que significa que no se introducirán nuevos subprocesos al hacer la llamada, y cuando se usa en combinación con await
, cederá el control a la persona que llama hasta que se complete la solicitud de E / S. Esto escalará bien, ya que liberará el subproceso ThreadPool que invocó la operación para que trabaje más mientras se está ejecutando la solicitud, por lo que su servidor puede procesar más solicitudes mientras tanto.
Por el contrario, usar un hilo dedicado (sincronizar sobre asincronía) solo para hacer una llamada de solicitud de bloqueo de IO definitivamente no se escalará bien, y podría eventualmente causar una inanición si el tiempo de ejecución es lo suficientemente largo.
Editar
La verdadera naturaleza asíncrona de la implementación XXXAsync
proviene del controlador del dispositivo de red que proporciona un punto final asíncrono al sistema operativo. Bajo las cubiertas, la biblioteca WinHTTP
(Thanks @Noseratio for the correction) se usa para las operaciones asincrónicas. Lo que eso significa es que se genera un paquete de solicitud de E / S (IRP) y se pasa al controlador del dispositivo. Una vez que se complete la solicitud, se producirá una interrupción de la CPU que eventualmente hará que se invoque la devolución de llamada registrada. Puede ver estos ejemplos: Usar las funciones HTTP de WinInet en modo asíncrono completo o Windows con C ++ - WinHTTP asincrónico para ejemplos asincrónicos, y por supuesto leer el excelente No hay subproceso de Stephan Cleary. Puede implementarlo de forma nativa usted mismo y envolverlo en un contenedor administrado.