2010-08-06 14 views
11

Tengo una tarea que inicia varias tareas secundarias. (Por ejemplo, la Tarea A crea B, C, D, E, F). También creo un System.Threading.Timer para sondear una base de datos cada 10 segundos para verificar si el elemento programado fue cancelado por solicitud. Si lo hace, establece CancellationTokenSource para que la tarea sepa cancelar. Cada subtarea, en este caso B, C, D, E, F, se cancelará cuando corresponda (están haciendo un bucle a través de archivos y moviéndolos).¿Cuándo deshacerse de System.Threading.Task con tareas secundarias?

Desde Task implementos IDisposable, quiero saber si es una buena idea llamar a Task.WaitAll de nuevo desde el bloque catch, que esperar a que las cancelaciones a propagar. Mientras se procesa la solicitud de cancelación, las sub-tareas pueden estar en el medio de un bucle y no se pueden cancelar hasta que completa

Sin embargo, por MSDN:

llamar siempre a botar antes de soltar su última referencia a la Tarea. De lo contrario, los recursos que está utilizando no se liberarán hasta que el recolector de elementos no utilizados llame al método Finalizar del objeto Tarea.

¿Debo llamar a esperar de nuevo en mi serie de tareas con el fin de llamar adecuadamente Dispose() en cada tarea en la matriz?

public class MyCancelObject 
{ 
    CancellationTokenSource Source { get;set;} 
    int DatabaseId { get;set;} 
} 

private void CheckTaskCancelled(object state) 
{ 
    MyCancelObject sourceToken = (MyCancelObject)state; 

    if (!sourceToken.CancelToken.IsCancellationRequested) 
    { 
    //Check database to see if cancelled -- if so, set to cancelled 
    sourceToken.CancelToken.Cancel(); 
    } 
} 

private void SomeFunc() 
{ 
    Task.StartNew(() => 
    { 
    MyCancelObject myCancelObject = new MyCancelObject(
     databaseId, 
     new CancellationTokenSource()); 
    System.Threading.Timer cancelTimer = new Timer(
     new TimerCallback(CheckIfTaskCancelled), 
     myCancelObject, 
     10000, 
     10000);   
    Task[] someTasks = new Task[someNumberOfTasks]; 

    for (int i = 0; i < someNumberOfTasks; i++) 
     someTasks[i] = Task.Factory.StartNew(
     () => 
     { 
      DoSomeWork(someObject, myCancelObject.CancelToken.Token); 
     }, 
     TaskCreationOptions.AttachedToParent | TaskCreationOptions.LongRunning, 
     myCancelObject.CancelToken.Token); 

    try 
    { 
     Task.WaitAll(someTasks, cts); 
    } 
    catch (AggregateException) 
    { 
     //Do stuff to handle 
    } 
    catch (OperationCanceledException) 
    { 
     //Should I call Task.WaitAll(someTasks) again?? 
     //I want to be able to dispose. 
    } 
    } 
} 
+0

Probé un código y encontré que 'Task.WaitAll' se puede llamar de nuevo para esperar de forma segura las tareas que aún se pueden estar ejecutando. Después de eso, pueden ser eliminados. Sin embargo, me gustaría saber si hay otras formas de manejar esto. –

+2

No solo es aceptable para su propia pregunta, sino que se recomienda. Siéntete libre de publicar una respuesta y aceptarla si sientes que la has descubierto. –

Respuesta

2

Me siento como si hubiera descubierto esto, pero cualquiera que quiera agregar algo útil es más que bienvenido.

Simplemente llamé Task.WaitAll() nuevamente desde el bloque catch para esperar a que finalicen las otras tareas. Después de que todos hayan terminado, finalmente he bloqueado todas las tareas de la matriz.

try 
{ 
Task.WaitAll(someTaskArray, cancelToken) 
} 
catch (OperationCanceledException) 
{ 
Task.WaitAll(someTaskArray); 
} 
finally 
{ 
for (int i = 0; i < someTaskArray.Length; i++) 
someTaskArray[i].Dispose(); 
} 
+0

El mío se siente peor que eso, para poder esperar tuve que: try {Tasks.WaitAll (tasks); } catch (OperationCanceledException) { try {Tasks.WaitAll (tasks); } catch (AggregateException) {Tasks.WaitAll (tasks); } } Entonces deseche ... No estoy seguro por qué, pero parece que funciona. – Tom

Cuestiones relacionadas