2012-05-02 30 views
11

Moje pytanie brzmi: jak mogę zatrzymać długo działające zadanie (.net 4)? Wdrożyłem TPL i próbowałem używać CancellationTokenSource, ale nie działa to w moim scenariuszu. Wszystkie przykłady, które widziałem, zakładają, że wykonujesz pracę w pętli while, dzięki czemu możesz sprawdzić, czy zadanie zostało anulowane, a ja mam tylko jedną operację, która zajmuje dużo czasu. Nie mogę się doczekać ukończenia pracy, ponieważ muszę założyć, że może ona nigdy się nie zakończyć. Oto kod próbowałem:Bezpiecznie zatrzymaj długotrwałe zadanie

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

Kod działa, ale zadanie nie jest usuwane, jeśli „timeout” dzieje i że praca jest wykonywana dostęp tj zasoby „niezarządzanego kodu”. Powiedział, że IsCancelledRequested nie może być użyty w Work.LongRunning(), więc nie mogę ThrowIfCancellationRequested.

Jestem otwarty na inne pomysły, a także próbowałem BackgroundWorker, ale to też nie wydaje się pasować.

New przykład:

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

Odpowiedz

2

Task Parallel Library przeznaczony jest do intensywnej pracy procesora. Intensywne działanie procesora odbywa się przez chwilę. Jeśli twój Work.LongRunning() jest intensywnie obciążony procesorem, powinieneś móc przekazać token anulowania wewnątrz i anulować go. Jeśli nie jest to procesor intensywny, możesz po prostu odrzucić wynik w ewentualnym wywołaniu zwrotnym i nie przejmować się zatrzymaniem aktualnej pracy, ponieważ tylko czeka.

BTW, jeśli czekałeś (na wywołanie bazy danych lub coś podobnego) prawdopodobnie masz gdzieś na dole metody asynchroniczne. Możesz ułożyć wzór Początek/Koniec i zawinąć go w Zadanie. To pytanie wyjaśnia, w jaki sposób: TPL TaskFactory.FromAsync vs Tasks with blocking methods W ten sposób unikniesz hogowania wątku ogólnego przeznaczenia, ponieważ czekanie IO jest wykonywane w specjalny sposób obsługiwany przez system operacyjny.

+0

Dzięki dla odpowiedzi! Spojrzałem na wzorzec Begin \ End, ale potem, aby sprawdzić wynik na Koniec ((Zadanie ) isyncResult) .Zaznacz, że musisz poczekać, aż zakończy się. Zrobiłem aktualizację na moim przykładzie. Ponieważ właściwości kontrolera ServiceController wydają się być leniwym ładowaniem, gdy tylko sprawdzisz wartość, może to potrwać jak długo ... – nickv

+0

Muszę przyznać, że nie mogę całkowicie przestrzegać twojego kodu, ale dlaczego nie dodajesz kontynuacji z ContinueWith? ? – Stilgar

+0

Ponieważ nie chcę kontynuować nowego zadania. Mam jedno zadanie, które chcę wykonać, zadanie to nie może uzyskać odpowiedzi z powodu tego, że zadanie będzie w stanie działającym na zawsze, nawet jeśli wywołano Anulowane. – nickv

2

Oto przykład w wariancie 1 opisano obove (to tylko zabijania zadanie bez sygnalizacji odwołania)

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

W rzeczywistości ten kod nie zabija długo działającego zadania doSomething. Kiedy naciśniesz "c", otrzymasz "Zadanie zostało anulowane", ale zadanie doSomething nadal działa. Po prostu poczekaj 30 sekund, a otrzymasz komunikat "obudził się" w konsoli. –