whenall - task httpresponsemessage c#
¿Cómo escribir un método "a la espera"? (5)
¿Cómo escribiría mi propio método "a la espera"? ¿Es tan simple como envolver el código que quiero ejecutar de forma asíncrona en una
Task
y devolver eso?
Esa es una opción, pero lo más probable es que no sea lo que quiere hacer, porque en realidad no le ofrece muchas de las ventajas del código asíncrono. Para obtener más detalles, consulte ¿Debería exponer las envolturas asincrónicas de Stephen Toub para métodos síncronos?
En general, los métodos no son esperables, los tipos son. Si desea poder escribir algo como await MyMethod()
, MyMethod()
debe devolver Task
, Task<T>
o un tipo de await
personalizado. Usar un tipo personalizado es un escenario raro y avanzado; usando Task
, tienes varias opciones:
- Escribe tu método usando
async
yawait
. Esto es útil para componer acciones de forma asincrónica, pero no se puede usar para las llamadas enawait
más internas. - Cree la
Task
usando uno de los métodos enTask
, comoTask.Run()
oTask.FromAsync()
. - Use
TaskCompletionSource
. Este es el enfoque más general, se puede usar para crear métodos deawait
de todo lo que sucederá en el futuro.
Finalmente estoy investigando las palabras clave async & await, que como que "entiendo", pero todos los ejemplos que he visto llaman a los métodos asincrónicos en .Net Framework, por ejemplo, este , que llama a HttpClient.GetStringAsync()
.
Lo que no tengo tan claro es qué sucede con un método así, y cómo escribiría mi propio método "a la espera". ¿Es tan simple como envolver el código que quiero ejecutar de forma asíncrona en una tarea y devolver eso?
... cómo escribiría mi propio método "a la espera".
Devolver una Task
no es la única forma. Tienes una opción para crear un awaiter personalizado (implementando GetAwaiter
e INotifyCompletion
), aquí hay una gran lectura: "Espere cualquier cosa" . Ejemplos de API .NET que devuelven servidores personalizados: Task.Yield()
, Dispatcher.InvokeAsync
.
Tengo algunas publicaciones con esperadores personalizados here y here , por ejemplo:
// don''t use this in production
public static class SwitchContext
{
public static Awaiter Yield() { return new Awaiter(); }
public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
{
public Awaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
}
public void GetResult() { }
}
}
// ...
await SwitchContext.Yield();
Es tan simple como
Task.Run(() => ExpensiveTask());
Para que sea un método a la espera:
public Task ExpensiveTaskAsync()
{
return Task.Run(() => ExpensiveTask());
}
Lo importante aquí es devolver una tarea. El método ni siquiera tiene que marcarse como sincronización. (Solo lea un poco más para que aparezca en la imagen)
Ahora esto se puede llamar como
async public void DoStuff()
{
PrepareExpensiveTask();
await ExpensiveTaskAsync();
UseResultsOfExpensiveTask();
}
Tenga en cuenta que aquí la firma del método dice async
, ya que el método puede devolver el control al llamador hasta que vuelva a aparecer ExpensiveTaskAsync()
. Además, costoso en este caso significa mucho tiempo, como una solicitud web o similar. Para enviar cálculos pesados a otro hilo, generalmente es mejor usar los enfoques "antiguos", es decir, System.ComponentModel.BackgroundWorker
para aplicaciones GUI o System.Threading.Thread
.
Sí, técnicamente solo necesita devolver una Task
o Task<Result>
desde un método async
para implementar un método que se pueda esperar.
Esto admite el patrón asincrónico basado en tareas .
Sin embargo, hay varias maneras de implementar el TAP. Consulte Implementación del patrón asincrónico basado en tareas para obtener más detalles.
(Pero todas estas implementaciones aún devuelven Task
o Task<Result>
, por supuesto).
Simplemente convierta su método en Tarea. como @Romiox Yo suelo usar esta extensión
public static partial class Ext
{
#region Public Methods
public static Task ToTask(Action action)
{
return Task.Run(action);
}
public static Task<T> ToTask<T>(Func<T> function)
{
return Task.Run(function);
}
public static async Task ToTaskAsync(Action action)
{
await Task.Run(action);
}
public static async Task<T> ToTaskAsync<T>(Func<T> function)
{
return await Task.Run(function);
}
#endregion Public Methods
}
Ahora, digamos que tienes
void foo1 ()
void foo2 (int i1)
int foo3 ()
int foo4 (int i1)
... Entonces puedes declarar tu [método asíncrono] como @Romiox
async Task foo1Async(){
return await Ext.ToTask(()=>foo1());
}
async Task foo2Async(int i1){
return await Ext.ToTask(()=>foo2(i1));
}
async Task<int> foo3Async(){
return await Ext.ToTask(()=>foo3());
}
async Task<int> foo4Async(int i1){
return await Ext.ToTask(()=>foo4(i1));
}
O
async Task foo1Async(){
return await Ext.ToTaskAsync(()=>foo1());
}
async Task foo2Async(int i1){
return await Ext.ToTaskAsync(()=>foo2(i1));
}
async Task<int> foo3Async(){
return await Ext.ToTaskAsync(()=>foo3());
}
async Task<int> foo4Async(int i1){
return await Ext.ToTaskAsync(()=>foo4(i1));
}
...
Ahora usa el modo asincrónico y espera por cualquiera de fooAsync, por ejemplo, foo4Async
async Task<int> TestAsync()
{
///Initial Code
int m=3;
///Call the task
var X =foo4Async(m);
///Between
///Do something while waiting comes here
///..
var Result =await X;
///Final
///Some Code here
return Result;
}