run method español create await async c# performance async-await theory

method - task c#



¿Async aguarda rendimiento? (5)

¿Puede ocurrir tal escenario?

Absolutamente. Por este motivo, debe ser concienzudo acerca de dónde utiliza el código asíncrono. Por lo general, es mejor usarlo para métodos que realmente realicen una operación asíncrona (por ejemplo, disco o E / S de red). El tiempo que llevan estas operaciones generalmente supera con creces el costo de programar tareas en subprocesos. Además, a nivel del sistema operativo, este tipo de operaciones son inherentemente asíncronas, por lo que en realidad está eliminando una capa de abstracción utilizando métodos asíncronos.

Sin embargo, incluso en estos casos, es probable que no vea una diferencia notable en el rendimiento al cambiar al código asíncrono a menos que pueda aprovechar la concurrencia. Por ejemplo, el código que publicaste probablemente no verá una ganancia real de rendimiento a menos que se cambie a algo como esto:

await Task.WhenAll(new[]{A(), B(), C(), D(), ...});

(Solo una pregunta teórica - para aplicaciones que no sean GUI)

Asumiendo que tengo este código con muchas awaits :

public async Task<T> ConsumeAsync() { await A(); await b(); await c(); await d(); //.. }

Donde cada tarea puede tomar un período de tiempo muy corto,

Pregunta (de nuevo, teórica)

Podría haber una situación en la que el tiempo total se ocupe de todos esos "liberar hilos" y "recuperar hilos" (rojo y verde aquí :)

Está tomando más tiempo que un solo hilo que podría hacer todo el trabajo con un poco de retraso,

Quiero decir, quería ser el más productivo, pero en lugar de eso, ya que todos esos conmutadores iban y venían, realmente perdía productividad.

¿Puede ocurrir tal escenario?


Sí, claro que puede pasar. Con toda la sobrecarga de la creación de la máquina de estados, el control de ida y vuelta y el uso de subprocesos IOCP . Pero como se ha dicho, la TPL está bastante optimizada. Por ejemplo, no olvidemos que si TaskAwaitable finaliza rápidamente, es posible que no haya sobrecarga y se ejecute de forma síncrona, lo que puede suceder a menudo con operaciones rápidas.


Sí, en teoría. Normalmente no, en el mundo real.

En el caso común, async se usa para operaciones de E / S vinculadas, y la sobrecarga de la administración de subprocesos es indetectable en comparación con ellas. La mayoría de las veces, las operaciones asíncronas toman mucho tiempo (en comparación con la administración de subprocesos) o ya se han completado (por ejemplo, un caché). Tenga en cuenta que async tiene una "ruta rápida" que se activa si la operación ya está completada, donde no produce el hilo.

Para obtener más información, consulte el Zen de Async y msdn.microsoft.com/en-us/magazine/hh456402.aspx .


Sí, puede pasar. Además, no olvide que, toda la eficiencia allí que puede programar, el sistema de tareas SÍ tiene gastos generales.

Si obtienes demasiado granulkar con algo como esto, la sobrecarga de sincronización PUEDE matarte. LO QUE DICE: Las tareas están programadas de manera bastante eficiente.

Pero la vieja regla se pega: no vayas super granular. A veces la optimización ayuda.


Un objeto de Task representa el resultado diferido de una operación pendiente. No tiene que usar tareas y async/await si no tiene ninguna operación pendiente. De lo contrario, creo que el código async / await es generalmente más eficiente que su simple TPL ContinueWith analógico.

Vamos a hacer un tiempo:

using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication { class Program { // async/await version static async Task<int> Test1Async(Task<int> task) { return await task; } // TPL version static Task<int> Test2Async(Task<int> task) { return task.ContinueWith( t => t.Result, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } static void Tester(string name, Func<Task<int>, Task<int>> func) { var sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (int i = 0; i < 10000000; i++) { func(Task.FromResult(0)).Wait(); } sw.Stop(); Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds); } static void Main(string[] args) { Tester("Test1Async", Test1Async); Tester("Test2Async", Test2Async); } } }

La salida:

Test1Async: 1582ms Test2Async: 4975ms

Por lo tanto, de manera predeterminada, las continuaciones se manejan de manera más eficiente que las continuaciones con ContinueWith Optimicemos ligeramente este código:

// async/await version static async Task<int> Test1Async(Task<int> task) { if (task.IsCompleted) return task.Result; return await task; } // TPL version static Task<int> Test2Async(Task<int> task) { if (task.IsCompleted) return Task.FromResult(task.Result); return task.ContinueWith( t => t.Result, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); }

La salida:

Test1Async: 1557ms Test2Async: 429ms

Ahora gana la versión no asíncrona. En el caso de la versión async , creo que esta optimización ya se ha realizado internamente mediante la infraestructura async/await .

De todos modos, hasta ahora solo hemos tratado con tareas completadas ( Task.FromResult ). Introduzcamos la asincronía real (naturalmente, haremos menos iteraciones esta vez):

static Task<int> DoAsync() { var tcs = new TaskCompletionSource<int>(); ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0)); return tcs.Task; } static void Tester(string name, Func<Task<int>, Task<int>> func) { ThreadPool.SetMinThreads(200, 200); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (int i = 0; i < 1000000; i++) { func(DoAsync()).Wait(); } sw.Stop(); Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds); }

La salida:

Test1Async: 4207ms Test2Async: 4734ms

Ahora la diferencia es muy marginal, aunque la versión async todavía funciona un poco mejor. Sin embargo, creo que tal ganancia es realmente despreciable, comparable al costo real de la operación asíncrona o al costo de restaurar el contexto capturado para cuando SynchronizationContext.Current != null .

La conclusión es que, si trabaja con tareas asíncronas, vaya a async / await si tiene una opción, no por razones de rendimiento sino por facilidad de uso, legibilidad y mantenimiento.