method - httpclient c# async
Arquitectura para async/await (2)
¿es ineficiente ya que básicamente estás creando un nuevo hilo para cada capa (llamando asíncronamente a una función asincrónica para cada capa, o realmente no importa y depende de tus preferencias?
No. Los métodos asincrónicos no necesariamente usan nuevos hilos. En este caso, dado que la llamada al método asincrónico subyacente es un método vinculado a IO, realmente no debería haber nuevos hilos creados.
Es esto:
1. necessary
Es necesario "burbujear" las llamadas asincrónicas si desea mantener la operación asincrónica. Sin embargo, esto es realmente lo que se prefiere, ya que le permite aprovechar al máximo los métodos asíncronos, lo que incluye componerlos juntos en toda la pila.
2. negative on performance
No. Como mencioné, esto no crea nuevos hilos. Hay algunos gastos generales, pero gran parte de esto se puede minimizar (ver más abajo).
3. just a matter of preference
No si quieres mantener esto asincrónico. Debe hacer esto para mantener las cosas asincrónicas en la pila.
Ahora, hay algunas cosas que puede hacer para mejorar perf. aquí. Si solo está ajustando un método asíncrono, no necesita usar las funciones de idioma, solo devuelva la Task
:
public virtual Task Save()
{
return repository.Save();
}
El método repository.Save()
ya devuelve una Task
- no necesita esperar solo para envolverla en una Task
. Esto mantendrá el método algo más eficiente.
También puede hacer que sus métodos asincrónicos de "bajo nivel" usen ConfigureAwait
para evitar que necesiten el contexto de sincronización de llamadas:
private async Task<string> Dosomething2()
{
//other stuff
...
return await Dosomething3().ConfigureAwait(false);
}
Esto reduce drásticamente la sobrecarga involucrada en cada await
si no tiene que preocuparse por el contexto de la llamada . Esta suele ser la mejor opción cuando se trabaja con código de "biblioteca", ya que la await
"externa" capturará el contexto de la interfaz de usuario. El funcionamiento "interno" de la biblioteca no suele preocuparse por el contexto de sincronización, por lo que es mejor no capturar eso.
Finalmente, advierto contra uno de tus ejemplos:
private async Task<string> Dosomething3()
{
//other stuff
...
// Potentially a bad idea!
return await Task.Run(() => "");
}
Si está haciendo un método asíncrono que, internamente, está utilizando Task.Run
para "crear asincronía" alrededor de algo que no es asíncrono, está terminando efectivamente el código síncrono en un método asíncrono. Esto utilizará un hilo ThreadPool, pero puede "ocultar" el hecho de que lo está haciendo, haciendo que la API sea engañosa. A menudo es mejor dejar la llamada a Task.Run
para sus llamadas de más alto nivel, y dejar que los métodos subyacentes permanezcan sincrónicos a menos que sean realmente capaces de aprovechar el IO asíncrono o algunos medios de descarga que no sean Task.Run
. (Esto no siempre es cierto, pero el código "asincrónico" envuelto sobre el código sincrónico a través de Task.Run
, y luego devuelto a través de async / Task.Run
menudo es un signo de un diseño defectuoso).
Si está utilizando async / await en un nivel inferior en su arquitectura, ¿es necesario "aumentar" las llamadas async / aguardar hasta arriba, es ineficiente ya que básicamente está creando un nuevo hilo para cada capa (llamada asíncrona una función asincrónica para cada capa, ¿o realmente no importa y solo depende de tu preferencia?
Estoy usando EF 6.0-alpha3 para poder tener métodos asíncronos en EF.
Mi repositorio es tal:
public class EntityRepository<E> : IRepository<E> where E : class
{
public async virtual Task Save()
{
await context.SaveChangesAsync();
}
}
Ahora mi capa de negocios es como tal:
public abstract class ApplicationBCBase<E> : IEntityBC<E>
{
public async virtual Task Save()
{
await repository.Save();
}
}
Y luego, por supuesto, mi método en mi UI tendría el siguiente patrón al llamar.
Es esto:
- necesario
- negativo en el rendimiento
- solo una cuestión de preferencia
Incluso si esto no se usa en capas / proyectos separados, las mismas preguntas se aplican a si estoy llamando a métodos anidados en la misma clase:
private async Task<string> Dosomething1()
{
//other stuff
...
return await Dosomething2();
}
private async Task<string> Dosomething2()
{
//other stuff
...
return await Dosomething3();
}
private async Task<string> Dosomething3()
{
//other stuff
...
return await Task.Run(() => "");
}
Si está utilizando async / await en un nivel inferior en su arquitectura, ¿es necesario "aumentar" las llamadas async / aguardar hasta arriba, es ineficiente ya que básicamente está creando un nuevo hilo para cada capa (llamada asíncrona una función asincrónica para cada capa, ¿o realmente no importa y solo depende de tu preferencia?
Esta pregunta sugiere un par de áreas de malentendido.
En primer lugar, no crea un nuevo hilo cada vez que llama a una función asincrónica.
En segundo lugar, no necesita declarar un método asíncrono, simplemente porque está llamando a una función asíncrona. Si está satisfecho con la tarea que ya se está devolviendo, solo devuelva eso de un método que no tiene el modificador asíncrono:
public class EntityRepository<E> : IRepository<E> where E : class
{
public virtual Task Save()
{
return context.SaveChangesAsync();
}
}
public abstract class ApplicationBCBase<E> : IEntityBC<E>
{
public virtual Task Save()
{
return repository.Save();
}
}
Esto será un poco más eficiente, ya que no implica la creación de una máquina de estado por muy pocas razones, pero lo más importante es que es más simple.
Cualquier método asíncrono en el que tengas una única expresión de espera en espera de una Task
o Task<T>
, justo al final del método sin procesamiento adicional, sería mejor escribirlo sin usar async / await. Así que esto:
public async Task<string> Foo()
{
var bar = new Bar();
bar.Baz();
return await bar.Quux();
}
está mejor escrito como:
public Task<string> Foo()
{
var bar = new Bar();
bar.Baz();
return bar.Quux();
}
(En teoría, existe una diferencia muy pequeña en las tareas que se crean y, por lo tanto, las personas que llaman pueden agregarle continuidades, pero en la gran mayoría de los casos, no notará ninguna diferencia).