2010-08-11 23 views
22

Actualmente estoy reemplazando algunas funciones tarea recién hecho con una nueva aplicación mediante la funcionalidad de nuevos System.Threading.Tasks encontrado en .NET 4.Como llegar una notificación de que ha completado System.Threading.Tasks.Task

Sin embargo, tengo un problema leve, y aunque puedo pensar en algunas soluciones, me gustaría obtener algunos consejos sobre cuál es la mejor manera de hacerlo, y si me falta un truco en alguna parte.

Lo que necesito es que un proceso arbitrario sea capaz de iniciar una tarea pero luego continuar y no esperar a que la tarea termine. No es un problema, pero cuando necesito hacer algo con el resultado de una tarea, no estoy seguro de la mejor manera de hacerlo.

Todos los ejemplos que he visto utilizan Wait() en la tarea hasta que se completa o hace referencia al parámetro Result en la tarea. Ambos bloquearán el hilo que inició la Tarea, que no quiero.

Algunas soluciones que he pensado:

  1. Crear un nuevo hilo y empezar la tarea en la que, a continuación, utilizar wait() o .Result para bloquear el nuevo hilo y sincronizar el resultado de nuevo a la persona que llama de alguna manera, posiblemente con una encuesta al parámetro IsCompleted de las tareas.

  2. Tengo una tarea 'Notificar finalizado' que puedo iniciar después de la finalización de la tarea que quiero ejecutar, que luego genera un evento estático o algo así.

  3. Pase un delegado a la entrada de la tarea y llámelo para notificar que la tarea ha finalizado.

puedo pensar o pros y los contras de todos ellos, pero sobre todo no me gusta la idea de tener que crear explícitamente un nuevo hilo para iniciar la tarea cuando el uno de los objetivos de la utilización de la La clase de tarea en primer lugar es abstraerse del uso directo de subprocesos.

¿Alguna idea sobre la mejor manera? ¿Me estoy perdiendo algo simple? ¿Sería demasiado pedir un evento 'Completado' :)? (Seguro que hay una buena razón por la cual no hay una sola!)

+0

Por curiosidad, ¿por qué no utilizar BackgroundWorker? Tiene la finalización de la tarea, así como la notificación de cancelación. – Robaticus

+1

'BackgroundWorker' ya no es necesario ahora que' Task' está con nosotros. BGW requería un 'SynchronizationContext', que es opcional con' Task' (permitiendo un mayor paralelismo). –

Respuesta

26

I sospechoso que busca Task.ContinueWith (o Task<T>.ContinueWith). Básicamente dicen: "Cuando hayas terminado esta tarea, ejecuta esta acción". Sin embargo, hay varias opciones que puede especificar para tener más control sobre él.

MSDN entra en muchos más detalles sobre esto en "How to: Chain Multiple Tasks With Continuations" y "Continuation Tasks".

+0

Sí, lo entiendo, pero no resuelve el problema de saber cuándo ha finalizado la secuencia de tareas sin llamar a Task.Wait() (y por lo tanto a bloquear) en el hilo que inició la tarea. Un enfoque en el que pensé (como el anterior) fue utilizar Task.ContinueWith para encadenar una tarea que plantea un evento al que las partes interesadas pueden suscribirse. Esto mantendrá la tarea original atómica. –

+1

@bwilliams: ¿Por qué no expones la tarea y las partes interesadas pueden llamar a 'Task.ContinueWith'? No veo de qué manera * no * resuelve el problema de saber cuándo se ha completado la tarea ... es una llamada sin bloqueo que dice: "Devuélveme la llamada cuando haya terminado esto". –

+1

(O en lugar de exponer la tarea en sí, puede proporcionar su propio método que luego simplemente llama a 'ContinueWith' internamente. Es lo mismo, realmente) –

4

Puede aplicar task continuation.

Alternativamente, Task implementa IAsyncResult, por lo que puede utilizar el standard approaches for that interface (bloqueo, la votación, o esperando en su WaitHandle).

+0

Para el tema de continuación de la tarea, por favor mira mi comentario bajo la publicación de Jon Skeet. Bloqueo y sondeo No me gusta la idea, ya que significará tener que iniciar otro hilo para mantener el hilo original que inició la tarea. Vi que podía usar WaitHandle, pero la documentación dice que la forma preferida es usar Task.Wait(). Dicho esto, como eso no hace lo que quiero, tal vez el WaitHandle es lo mejor para mí –

4

En C# moderno, ya no es necesario llamar al ContinueWith() explícitamente. Una alternativa a la respuesta original aceptada sería simplemente crear un método async que await s el en cuestión, y haga lo que quiera cuando el Task finalice.

Por ejemplo, supongamos que desea plantear un evento llamado TaskCompleted cuando finaliza el Task. Debería escribir un método como:

async Task RaiseEventWhenTaskCompleted(Task task) 
{ 
    await task; 
    TaskCompleted?.Invoke(this, EventArgs.Empty); 
} 

Para "registrar" la espera, simplemente llame al método anterior. Agregue el manejo de excepciones como desee, ya sea en el método anterior o en algún código que eventualmente observe el Task devuelto por el método anterior.

Cuestiones relacionadas