2012-05-02 12 views
11

La mia domanda è: come posso interrompere un'attività a esecuzione prolungata (.net 4)? Ho implementato TPL e ho provato ad usare CancellationTokenSource ma non sembra funzionare per il mio scenario. Tutti gli esempi che ho visto presuppongono che tu stia lavorando in un ciclo while in modo da poter verificare se l'attività è stata annullata, mentre ho solo una singola operazione che richiede molto tempo. Non vedo l'ora che il lavoro sia completato, perché devo presupporre che non possa mai essere completato. Ecco il codice che ho provato:Arresto sicuro dell'attività a esecuzione lunga

 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."); 
     } 

Il codice funziona ma il compito non è mai disposta se il “timeout” accade e il lavoro che si sta facendo accessi “codice non gestito” vale a dire le risorse. Detto questo, IsCancelledRequested non può essere utilizzato in Work.LongRunning(), quindi non posso ThrowIfCancellationRequested.

Sono aperto ad altre idee così come ho provato con BackgroundWorker ma anche questo non sembra adattarsi.

nuovo esempio: Parallel Library

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(); 
     } 

risposta

2

Task è stato progettato per un lavoro intensivo della CPU. Il lavoro intensivo della CPU viene svolto in un attimo. Se Work.LongRunning() ha un utilizzo intensivo della CPU, dovresti essere in grado di passare il token di cancellazione all'interno e annullarlo. Se non è un uso intensivo della CPU, puoi semplicemente scartare il risultato in una eventuale richiamata e non preoccuparti di interrompere il lavoro effettivo poiché è solo in attesa.

BTW se si sta aspettando (per una chiamata al database o qualcosa del genere) probabilmente si hanno metodi asincroni da qualche parte nella parte inferiore. Puoi far ruotare il pattern Begin/End e avvolgerlo in un'attività. Questa domanda spiega come: TPL TaskFactory.FromAsync vs Tasks with blocking methods In questo modo si evita di utilizzare un thread generico poiché l'attesa dell'IO viene eseguita in un modo speciale gestito dal sistema operativo.

+0

Grazie per la risposta! Ho guardato il pattern Begin \ End, ma poi per controllare un risultato sul comando End ((Task ) isyncResult). Esegui il backup fino a quando non viene completato. Ho fatto un aggiornamento sul mio esempio. Poiché le proprietà sul ServiceController sembrano essere pigri caricati non appena si verifica un valore, potrebbe richiedere quanto mai lungo ... – nickv

+0

Devo ammettere che non riesco a seguire completamente il codice, ma perché non si aggiungono continuazioni con ContinueWith ? – Stilgar

+0

Perché non desidero continuare con una nuova attività. Ho un compito che voglio eseguire, quell'attività potrebbe non sostenere una risposta a causa di qualsiasi cosa così il compito sarà in uno stato di esecuzione per sempre anche se è stato chiamato Annullato. – nickv

2

Ecco un esempio per l'opzione 1 descritto obove (cioè solo uccidendo Task senza segnalare cancellazione)

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

In realtà, questo codice non elimina l'attività DoSomething a esecuzione prolungata. Quando si preme "c", viene visualizzato il messaggio "L'attività è stata annullata", ma l'attività doSomething viene comunque eseguita. Attendi solo 30 secondi e riceverai il testo "risvegliato ora" nella console. –

Problemi correlati