2012-05-02 62 views
11

我的問題是如何停止長時間運行的任務(.net 4)?我實施了TPL並嘗試使用CancellationTokenSource,但它似乎不適用於我的場景。我見過的所有例子都假設你正在while循環中工作,以便檢查任務是否已被取消,而我只需要一個操作需要很長時間。我不能等待工作完成,因爲我需要假設它可能永遠不會完成。 這是我曾嘗試代碼:安全地停止長時間運行的任務

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

代碼工作,但如果「超時」發生的和正在做的工作訪問「非託管代碼」,即資源的任務從來沒有佈置。也就是說,IsCancelledRequested不能用在Work.LongRunning()中,所以我不能ThrowIfCancellationRequested。

我對其他想法開放以及我已經嘗試過BackgroundWorker,但也似乎不適合。

新的例子:

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

回答

2

任務並行庫是專門爲CPU密集型工作。 CPU密集型工作在一段時間內完成。如果您的Work.LongRunning()是CPU密集型的,您應該可以在內部傳遞取消令牌並取消它。如果不是CPU密集型的,那麼你可以簡單地放棄最終回調的結果,而且不用擔心停止實際工作,因爲它只是在等待。

順便說一句,如果你有等待(對於數據庫調用或其他),你可能在底部的某個地方有異步方法。您可以繪製開始/結束模式並將其包裝到任務中。這個問題解釋瞭如何:TPL TaskFactory.FromAsync vs Tasks with blocking methods這樣你就可以避免佔用一般用途的線程,因爲IO等待是由OS處理的特殊方式完成的。

+0

感謝您的響應!我查看了Begin \ End模式,然後檢查End的結果((任務)isyncResult)。結果必須等到它完成。我對我的例子進行了更新。由於ServiceController上的屬性似乎在你檢查一個值時會被延遲加載,所以它可能需要很長的時間... – nickv

+0

我必須承認我無法完全遵循你的代碼,但爲什麼你不用ContinueWith添加延續? – Stilgar

+0

因爲我不想繼續新的任務。我有一個任務需要執行,即使已被調用Cancelled,任務也可能不會響應,因此任務將永遠處於運行狀態。 – nickv

2

這裏是選項1中描述obove一個例子(即,只是殺死任務無需信令取消)

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

實際上,這段代碼不會殺死長時間運行的doSomething任務。當你按「c」時,你會看到「任務已被取消」,但doSomething任務仍在運行。只需等待30秒,您就可以在控制檯中「醒來」了。 –