¿Tienes que poner Task.Run en un método para hacerlo asincrónico?

Estoy tratando de entender async aguarde en la forma más simple. Quiero crear un método muy simple que agrega dos números por el bien de este ejemplo, concedido, no es tiempo de procesamiento en absoluto, solo es cuestión de formular un ejemplo aquí.

Ejemplo 1

private async Task DoWork1Async() { int result = 1 + 2; } 

Ejemplo 2

 private async Task DoWork2Async() { Task.Run( () => { int result = 1 + 2; }); } 

Si espero DoWork1Async() se ejecutará el código de forma síncrona o asíncrona?

¿Debo ajustar el código de sincronización con Task.Run para que el método sea accesible Y asíncrono para no bloquear el subproceso de la interfaz de usuario?

Estoy intentando averiguar si mi método es una Task o devuelve una Task ¿Necesito ajustar el código con Task.Run para que sea asincrónico?

Pregunta estúpida, estoy seguro, pero veo ejemplos en la red donde las personas esperan un código que no tenga nada de asincronía y no Task.Run StartNew en una Task.Run o StartNew .

Primero, aclaremos algo de terminología: “asincrónico” ( async ) significa que puede devolver el control al hilo de llamada antes de que comience. En un método async , esos puntos de “rendimiento” están await expresiones.

Esto es muy diferente del término “asíncrono”, como (mal) utilizado por la documentación de MSDN durante años para significar “ejecuta en un hilo de fondo”.

Para confundir aún más el problema, async es muy diferente a “agitable”; hay algunos métodos async cuyos tipos de devolución no son esperables, y muchos métodos que devuelven tipos pendientes que no son async .

Suficiente sobre lo que no son ; esto es lo que son :

  • La palabra clave async permite un método asíncrono (es decir, permite expresiones de await ). async métodos async pueden devolver Task , Task o (si es necesario) void .
  • Cualquier tipo que sigue un cierto patrón puede ser aguardable. Los tipos disponibles más comunes son Task y Task .

Por lo tanto, si reformulamos su pregunta a “cómo puedo ejecutar una operación en un hilo de fondo de una manera que sea aguardable”, la respuesta es usar Task.Run :

 private Task DoWorkAsync() // No async because the method does not need await { return Task.Run(() => { return 1 + 2; }); } 

(Pero este patrón es un enfoque pobre, ver más abajo).

Pero si su pregunta es “¿cómo creo un método async que puede devolver a su llamador en lugar de bloquear?”, La respuesta es declarar el método async y usar await sus puntos “ceder”:

 private async Task GetWebPageHtmlSizeAsync() { var client = new HttpClient(); var html = await client.GetAsync("http://www.example.com/"); return html.Length; } 

Entonces, el patrón básico de las cosas es tener un código async que dependa de “awaitables” en sus expresiones de await . Estos “awaitables” pueden ser otros métodos async o simplemente métodos regulares que devuelven awaitables. Los métodos habituales que devuelven Task / Task pueden usar Task.Run para ejecutar código en un hilo de fondo, o (más comúnmente) pueden usar TaskCompletionSource o uno de sus atajos ( TaskFactory.FromAsync , Task.FromResult , etc.) . No recomiendo envolver un método completo en Task.Run ; los métodos sincrónicos deben tener firmas sincrónicas, y se debe dejar al consumidor si se debe envolver en una Task.Run . Task.Run :

 private int DoWork() { return 1 + 2; } private void MoreSynchronousProcessing() { // Execute it directly (synchronously), since we are also a synchronous method. var result = DoWork(); ... } private async Task DoVariousThingsFromTheUIThreadAsync() { // I have a bunch of async work to do, and I am executed on the UI thread. var result = await Task.Run(() => DoWork()); ... } 

Tengo una intro async / await en mi blog; al final hay algunos buenos recursos de seguimiento. Los documentos de MSDN para async son inusualmente buenos.

Una de las cosas más importantes para recordar al decorar un método con asincrónico es que al menos hay un operador en espera dentro del método. En su ejemplo, lo traduciría como se muestra a continuación utilizando TaskCompletionSource .

 private Task DoWorkAsync() { //create a task completion source //the type of the result value must be the same //as the type in the returning Task TaskCompletionSource tcs = new TaskCompletionSource(); Task.Run(() => { int result = 1 + 2; //set the result to TaskCompletionSource tcs.SetResult(result); }); //return the Task return tcs.Task; } private async void DoWork() { int result = await DoWorkAsync(); } 

Cuando utiliza Task.Run para ejecutar un método, Task obtiene un hilo de threadpool para ejecutar ese método. Por lo tanto, desde la perspectiva del subproceso de la interfaz de usuario, es “asíncrono”, ya que no bloquea el subproceso de la interfaz de usuario. Esto está bien para la aplicación de escritorio, ya que normalmente no necesita muchos subprocesos para atender las interacciones del usuario.

Sin embargo, para la aplicación web, cada solicitud recibe servicio de un hilo de grupo de subprocesos y, por lo tanto, se puede boost el número de solicitudes activas guardando dichos subprocesos. El uso frecuente de subprocesos de subprocesos para simular la operación asincrónica no es escalable para las aplicaciones web.

True Async no implica necesariamente el uso de un subproceso para las operaciones de E / S, como acceso a archivos / bases de datos, etc. Puede leer esto para comprender por qué el funcionamiento de E / S no necesita subprocesos. http://blog.stephencleary.com/2013/11/there-is-no-thread.html

En su ejemplo simple, es un cálculo puro vinculado a la CPU, por lo que usar Task.Run está bien.