2012-04-06 18 views
6

Estoy intentando el async CTP, la versión 4.5 que permite utilizar métodos asíncronos sin necesidad de escribir los métodos Begin/End.Cuando el método devuelve vacío, ¿es lo mismo que una tarea?

Mi primera prueba es ejecutar un método asíncrono que devuelve vacío. Puedo ver algunos ejemplos y hacer lo siguiente:

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async void method01Async() 
{ 
    await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

En mi proyecto WPF tengo un cuadro de texto donde ver los resultados y un botón que ejecuta el método asíncrono.

En el método asíncrono, uso await, es necesario porque el método es asíncrono, y TasEx.Run para crear un nuevo hilo en el que ejecutar el código.

Mi duda es en este punto. En algunos ejemplos que veo de cómo crear un método asíncrono que devuelve vacío, use de esta manera, Task.Run o TaskEx.Run.

Si no me equivoco, Task.Run crea un nuevo hilo donde ejecutar el método. Entonces, ¿por qué usar un método asíncrono, si con la Tarea, crear un nuevo hilo, obtengo lo que quiero, no para bloquear el hilo principal?

Además, si el método asíncrono accede a alguna variable compartida, debo tener cuidado con la concurrencia, ¿verdad? Por lo tanto, no conozco la ventaja de utilizar métodos asíncronos, al menos en este caso.

De hecho, utilizo el mismo código sin async y sin esperar y el resultado es el mismo, el programa principal no está bloqueando y todo funciona como esperaba. El método es el siguiente:

private void method01Async() 
{ 
    TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("Exit method01Async: " + System.DateTime.Now); 
    }); 
} 

Mi pregunta es, esta es la forma correcta de utilizar asíncrono cuando el retorno void método?

+0

Por cierto, el Visual Studio 11 Beta está ahora fuera, que contiene las características de la CTP asíncrono, con algunas mejoras y correcciones de errores. – svick

+0

¿Y cuál es el beneficio de usar el método asincrónico en este caso, si en el método estoy usando una tarea, y la tarea usar un hilo niew? Si uso directamente una tarea, sin usar un método asíncrono, obtengo el comportamiento. Quizás los métodos asíncronos tengan más sentido si el método devuelve un valor y para los métodos nulos, ¿es mejor usar directamente una tarea? –

+0

Recomiendo encarecidamente que todos los métodos 'async' devuelvan' Tarea' o 'Tarea ' a menos que sean manejadores de eventos y * tengan * para devolver' void'. Esto le permite componerlos si es necesario y el manejo de errores también es más limpio (si un método 'async void' genera una excepción en un contexto WPF, se envía directamente al bucle de mensajes de UI, por lo que los métodos' async void' son * no * verdaderamente "dispara y olvida"). –

Respuesta

3

Si no estoy equivocado, Task.Run crear un nuevo hilo donde para ejecutar el método.

No exactamente. Task.Run() ejecutará el código en un subproceso diferente del subproceso de interfaz de usuario (al menos con el predeterminado TaskScheduler). Pero en realidad no creará un nuevo hilo en la mayoría de los casos, reutilizará un hilo existente del ThreadPool.

Entonces, ¿por qué utilizar un método asíncrono, si con la Tarea, crear un nuevo hilo, obtengo lo que quiero, no para bloquear el hilo principal?

El punto de async, en el contexto de una aplicación de interfaz de usuario, es ser capaz de ejecutar fácilmente un cierto código en el hilo de interfaz de usuario después de y operación asincrónica.

lo tanto, si usted hizo su method01Async “awaitable”, es decir, hecho que devolver un Task:

private async Task method01Async() 
{ 
    await Task.Run(/* whatever */); 
} 

A continuación, podría esperar que a partir del método btnAsync01_Click, si lo hizo `asíncrono:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after method01Async: " + System.DateTime.Now); 
} 

De esta forma, la última línea del método se ejecutará solo después de que se ejecute Task en method01Async. Y se ejecutará en el hilo de UI.

En .Net 4.0, se puede lograr un efecto similar usando ContinueWith() y Dispatcher.Invoke():

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async().ContinueWith(() => 
     Dispatcher.Invoke(
      new Action(() => 
       UpdateTxtLog("after method01Async: " + System.DateTime.Now))); 
} 

Estoy seguro de que estará de acuerdo en que esto es mucho más sucio y menos legible.

Además, si el método asíncrono accede a alguna variable compartida, debo tener cuidado con la concurrencia, ¿verdad?

Sí, tienes razón al respecto.

De hecho, utilizo el mismo código sin async y sin esperar y el resultado es el mismo, el programa principal no está bloqueando y todo funciona como esperaba.

El resultado ciertamente no es lo que pensé que debía hacer su código. La última línea de btnAsync01_Click, ejecutará "after method01Async", pero no esperará hasta que el Task iniciado en ese método finalice.


Como nota al margen, no hay necesidad de utilizar async en su method01Async. Volviendo al Task directamente (o no, si desea mantenerlo void -Volviendo), funcionará de la misma:

private Task method01Async() 
{ 
    return Task.Run(/* whatever */); 
} 
+0

Bueno, cuando dije que Task creaba un nuevo hilo, realmente estaba simplificando, sé que Task utiliza un hilo existente del threadpool, y si hay alguno libre, entonces la tarea debe esperar. En mi caso, no quiero esperar en el método principal (haga clic en evento en este caso) porque el método secundario (method01Async) solo hace algo que no se notificará al método principal. Por ejemplo, envía un correo electrónico a algo. Solo deseo enviar, pero no tener cuidado o notificar en la aplicación principal si la operación se realizó o no. –

+0

Es lo que estoy pensando, en los métodos nulos, tal vez asincrónico no es necesario, y los métodos asincrónicos se recomiendan para los métodos que devuelven un valor. –

+0

Si no quiere hacer nada después de que las operaciones se completen, entonces sí, no hay ninguna razón para usar 'await'. Aunque podría tener sentido utilizar el código interno de envío de correo electrónico para usar el grupo de subprocesos de manera más eficiente. – svick

1

En realidad, no está utilizando la función asíncrona, ya que no está esperando la llamada original. Aquí es cómo usted debe hacer es:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async Task method01Async() 
{ 
    return await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

Una vez que se cambie a esta (la parte importante de los cuales await method01Async() en su botón de evento de clic, saltará volver allí después de que salga, y el "después ethod01Async:" de registro de texto debe mostrar un segundo retraso de diez, al igual que su registro de "salida method01Async" en el método method01Async.

+0

Esto no se compilará. Debes hacer 'btnAsync01_Click'' async' si quieres usar 'await' en él. – svick

+0

Sí, es necesario para hacer clic en el evento asincrónico. Pero de esta manera, tengo la misma duda, si en el método01Async utilizo una tarea, estoy usando un nuevo hilo, por lo que no veo el beneficio de usar asincrónico. Eso me hace pensar que si los métodos asíncronos tienen más sentido cuando el método devuelve un resultado y para el método nulo tal vez sea mejor usar directamente un hilo usando una tarea. –

+0

@svick tiene razón. En realidad, no compilé esto, así que me voy de la memoria. El compilador le diría rápidamente que no puede llamar a 'await' desde un método no etiquetado con' async'. Editado para reflejar eso. Gracias svick. –

Cuestiones relacionadas