2012-05-02 18 views
11

Mi pregunta es ¿cómo puedo detener una tarea de ejecución larga (.net 4)? Implementé TPL e intenté usar CancellationTokenSource, pero parece que no funciona para mi escenario. Todos los ejemplos que he visto suponen que está trabajando en un ciclo while para que pueda verificar si la tarea se ha cancelado, mientras que solo tengo una operación única que requiere mucho tiempo. No puedo esperar a que se complete el trabajo, ya que necesito suponer que nunca se completará. Este es el código que he intentado:Detener con seguridad la tarea de larga ejecución

 bool? result = null; 

     var cs = new CancellationTokenSource(); 
     var ct = cs.Token; 

     var doWorkTask = new Task(() => 
     { 
      Console.WriteLine("start dowork task"); 

      result = Work.LongRunning(); 
     }, ct); 

     doWorkTask.Start(); 

     Task.WaitAny(new Task[] { doWorkTask }, timetowait); 

     if (doWorkTask.IsCompleted) 
     { 
     Console.WriteLine("dowork task completed"); 

      doWorkTask.Dispose(); 
     } 
     else 
     { 
     Console.WriteLine("dowork task has timedout"); 

      cs.Cancel(); 

      throw new TimeoutException("Timeout hit."); 
     } 

El código funciona, pero la tarea no está dispuesto si el “tiempo de espera” ocurre y el trabajo que se está realizando accesos “código no administrado” es decir los recursos. Dicho esto, el IsCancelledRequested no se puede usar en Work.LongRunning() así que no puedo ThrowIfCancellationRequested.

Estoy abierto a otras ideas, así como también lo he probado en BackgroundWorker, pero eso tampoco parece encajar.

Nueva ejemplo:

var service = new System.ServiceProcess.ServiceController(ServiceName, ServerName); 

     var serviceTask = Task.Factory.StartNew(() => 
     { 
      result = (service.Status == ServiceControllerStatus.Running 
       || service.Status == ServiceControllerStatus.StartPending); 
     }, cs.Token); 

     serviceTask.Wait(2000, cs.Token); 

     if (!serviceTask.IsCompleted) 
     { 
      cs.Cancel(); 
     } 

Respuesta

2

tareas de la Biblioteca paralelo está diseñado para el trabajo intensivo de la CPU. El trabajo intensivo de la CPU se realiza con un aspecto de tiempo. Si su Work.LongRunning() consume mucha CPU, debería poder pasar el token de cancelación y cancelarlo. Si no consume mucha CPU, simplemente puede descartar el resultado en una devolución de llamada eventual y no molestarse en detener el trabajo real, ya que solo está esperando.

BTW si tiene esperando (para una llamada a la base de datos o algo así) probablemente tenga métodos asincrónicos en algún lugar en la parte inferior. Puede visualizar el patrón Begin/End y envolverlo en una tarea. Esta pregunta explica cómo: TPL TaskFactory.FromAsync vs Tasks with blocking methods De esta manera evitará acaparar un hilo de propósito general ya que la espera de IO se realiza de una manera especial manejada por el sistema operativo.

+0

Gracias por la respuesta! Miré el patrón Begin \ End, pero luego verifiqué un resultado en End's ((Task ) isyncResult). Result que tienes que esperar hasta que finalice. Hice una actualización de mi ejemplo. Como las propiedades del ServiceController parecen estar cargadas de forma perezosa en cuanto compruebas un valor, puede que tarde un tiempo ... – nickv

+0

Debo admitir que no puedo seguir tu código por completo, pero ¿por qué no agregas continuaciones con ContinueWith? ? – Stilgar

+0

Porque no quiero continuar con una nueva tarea. Tengo una tarea que deseo realizar, esa tarea puede no generar una respuesta debido a lo que sea que la tarea estará en estado de ejecución para siempre, aunque se haya llamado a Cancelado. – nickv

2

Aquí se muestra un ejemplo para la opción 1 describe obove (es decir simplemente matar la tarea sin cancelación de señalización)

class Program 
    { 
     private static void Main(string[] args) 
     { 
      Test test = new Test(); 
      test.Run(); 

      Console.WriteLine("Type c to cancel"); 
      if (Console.ReadLine().StartsWith("c")) 
      { 
       Console.WriteLine("cancellation requested"); 
       test.CancellationTokenSource.Cancel(); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public class Test 
    { 
     private void DoSomething() 
     { 
      Console.WriteLine("DoSomething runs for 30 seconds "); 
      Thread.Sleep(new TimeSpan(0, 0, 0, 30)); 
      Console.WriteLine("woke up now "); 
     } 

     public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); 

     public void Run() 
     { 
       var generateReportsTask = Task.Factory.StartNew(() => 
       { 
        CancellationTokenSource.Token.ThrowIfCancellationRequested(); 
        Task doSomething = new Task(DoSomething, CancellationTokenSource.Token); 
        doSomething.Start(); 

        doSomething.Wait(CancellationTokenSource.Token); 
       }, CancellationTokenSource.Token); 

       generateReportsTask.ContinueWith(
        (t) => 
        { 
         if (t.Exception != null) 
          Console.WriteLine("Exceptions reported :\n " + t.Exception); 

         if (t.Status == TaskStatus.RanToCompletion) 
          Console.WriteLine("Completed report generation task"); 
         if (t.Status == TaskStatus.Faulted) 
          Console.WriteLine("Completed reported generation with unhandeled exceptions"); 
         if(t.Status == TaskStatus.Canceled) 
          Console.WriteLine("The Task Has been cancelled"); 
        }); 

     } 
    } 
+3

De hecho, este código no cancela la tarea doSomething de larga ejecución. Cuando presiona "c", obtendrá el mensaje "La tarea ha sido cancelada", pero la tarea doSomething aún se ejecuta. Solo espera 30 segundos y obtendrás el texto "despertar ahora" en la consola. –

Cuestiones relacionadas