2015-05-07 64 views
3

我正在開發一個概念驗證應用程序,該應用程序使用任務和信號量對數字列表進行分解,目前我有一個任務列表List<Task>,需要FactorNumberClass,然後計算目前FactorNumberClass內特定數字的因子正常工作。對於每個任務T,我有一個ContinueWith任務,用於更新總分數的進度,分解的平均時間,以及更新進度條的值(數字成功分解)/(需要分解的總數)。在將這些因子分解爲Tasks時,輸入SemaphoreSlim.Wait(cancelToken)將當前因子限制爲5個有效Tasks。最後,我有一個ContinueWhenAll,當所有任務完成時記錄。假設沒有取消,這一切都按我的意圖運作。檢測父級任務取消的正確方法是什麼?

當我嘗試取消任務時,出現問題,我無法檢測到任務是否已被取消,因此無法準確確定該號碼是否已成功分解或取消。如何檢測父任務是否被取消或運行完成?

取消標記定義:

public static CancellationTokenSource tokenSource = new CancellationTokenSource(); 
public static CancellationToken ct = tokenSource.Token; 

因子類代碼:

public class FactorNumberClass 
{ 
    public FactorNumberClass() 
    { 
    } 

    public FactorNumberClass(int num, int threadnum) 
    { 
     this.number = num; 
     this.threadNumber = threadnum; 
    } 

    public List<int> factors = new List<int>(); 
    public int number; 
    public int max; 
    public int threadNumber; 
} 

保理方法:

public void Factor(FactorNumberClass F, CancellationToken token) 
     { 
      LogtoStatusText("Thread: " + F.threadNumber + " Trying to enter semaphore"); 

      try 
      { 
       ASemaphore.Wait(ct); 

       F.max = (int)Math.Sqrt(F.number); //round down 

       for (int factor = 1; factor <= F.max; ++factor) 
       { //test from 1 to the square root, or the int below it, inclusive. 
        if (F.number % factor == 0) 
        { 
         F.factors.Add(factor); 
         if (factor != F.number/factor) 
         { 
          F.factors.Add(F.number/factor); 
         } 
        } 
       } 

       F.factors.Sort(); 
       Thread.Sleep(F.number * 300); 
       LogtoStatusText("Task: " + F.threadNumber + " Completed - Factors: " + string.Join(",", F.factors.ToArray())); 
       LogtoStatusText("Thread: " + F.threadNumber + " Releases semaphore with previous count: " + ASemaphore.Release()); 
      } 
      catch (OperationCanceledException ex) 
      { 
       LogtoStatusText("Thread: " + F.threadNumber + " Cancelled."); 
      } 
      finally 
      { 
      } 
     } 

方法開始處理:

public void btnStart_Click(object sender, RoutedEventArgs e) 
     { 
      Task T; 
      List<Task> TaskList = new List<Task>(); 
      LogtoStatusText("**** Begin creating tasks *****"); 
      s1.Start(); 

      AProject.FactorClassList.ForEach((f) => 
      { 
       T = new Task(((x) => { OnUIThread(() => { RunningTasks++; }); Factor(f, ct); }), ct); 

       T.ContinueWith((y) => 
       { 
        if (y.IsCompleted) 
        { 
         AProject.TotalProcessedAccounts++; 
         AProject.AverageProcessTime = (((Double)AProject.TotalProcessedAccounts/s1.ElapsedMilliseconds) * 1000); 
        } 
        OnUIThread(() => { RunningTasks--; }); 
        OnUIThread(() => { UpdateCounts(AProject); }); 
       }); 

       TaskList.Add(T); 
      }); 

      try 
      { 
       Task.Factory.ContinueWhenAll(TaskList.ToArray(), (z) => { LogtoStatusText("**** Completed all Tasks *****"); OnUIThread(() => { UpdateCounts(AProject); }); }); 
      } 
      catch (AggregateException a) 
      { 
       // For demonstration purposes, show the OCE message. 
       foreach (var v in a.InnerExceptions) 
        LogtoStatusText("msg: " + v.Message); 
      } 

      LogtoStatusText("**** All tasks have been initialized, begin processing *****"); 
      TaskList.ForEach(t => t.Start()); 
     } 

回答

0

我終於找到了我一直在尋找的解決方案將允許我推出(Start())我所有的Task對象,通過semaphoreslim運行它們,觀察CancellationToken,然後檢測Task是否被取消或正常完成。在這種情況下,如果Task已經進入信號量並且在CancellationTokenSource.Cancel()被觸發之前開始處理,則它只能「正常完成」。

這個答案:Elegantly handle task cancellation把我推向了正確的方向。我結束了追趕OperationCancelledException,記錄它,然後再扔吧,內ContinueWithTask

這裏被檢查更新的代碼,解決了我的問題

因子類:

private void Factor(FactorNumberClass F) 
     { 


      LogtoStatusText("Thread: " + F.threadNumber + " Trying to enter semaphore"); 

      try 
      { 
       ASemaphore.Wait(ct); 

       F.max = (int)Math.Sqrt(F.number); //round down 

       for (int factor = 1; factor <= F.max; ++factor) 
       { //test from 1 to the square root, or the int below it, inclusive. 
        if (F.number % factor == 0) 
        { 
         F.factors.Add(factor); 
         if (factor != F.number/factor) 
         { 
          F.factors.Add(F.number/factor); 
         } 
        } 
       } 

       F.factors.Sort(); 

       Thread.Sleep(F.number * 300); 

       LogtoStatusText("Task: " + F.threadNumber + " Completed - Factors: " + string.Join(",", F.factors.ToArray())); 

       LogtoStatusText("Thread: " + F.threadNumber + " Releases semaphore with previous count: " + ASemaphore.Release()); 
      } 
      catch 
      { 
       LogtoStatusText("Thread: " + F.threadNumber + " Cancelled"); 
       throw; 

      } 
      finally 
      { 

      } 

     } 

處理方法:

public void btnStart_Click(object sender, RoutedEventArgs e) 
{ 
    LaunchTasks(); 
} 

private void LaunchTasks() 
     { 
      Task T; 
      List<Task> TaskList = new List<Task>(); 

      LogtoStatusText("**** Begin creating tasks *****"); 

      s1.Start(); 

      AProject.FactorClassList.ForEach((f) => 
      { 
       T = new Task(((x) => { OnUIThread(() => { RunningTasks++; }); Factor(f); }), ct); 

       T.ContinueWith((y) => 
       { 
        if (y.Exception != null) 
        { 
         // LogtoStatusText(y.Status + " with "+y.Exception.InnerExceptions[0].GetType()+": "+ y.Exception.InnerExceptions[0].Message); 
        } 
        if (!y.IsFaulted) 
        { 

         AProject.TotalProcessedAccounts++; 
         AProject.AverageProcessTime = (((Double)AProject.TotalProcessedAccounts/s1.ElapsedMilliseconds) * 1000); 
        } 
        OnUIThread(() => { RunningTasks--; }); 
        OnUIThread(() => { UpdateCounts(AProject); }); 


       }); 

       TaskList.Add(T); 
      }); 

      try 
      { 
       Task.Factory.ContinueWhenAll(TaskList.ToArray(), (z) => { LogtoStatusText("**** Completed all Tasks *****"); OnUIThread(() => { UpdateCounts(AProject); }); }); 
      } 
      catch (AggregateException a) 
      { 
       // For demonstration purposes, show the OCE message. 
       foreach (var v in a.InnerExceptions) 
        LogtoStatusText("msg: " + v.Message); 
      } 

      LogtoStatusText("**** All tasks have been initialized, begin processing *****"); 

      TaskList.ForEach(t => t.Start()); 
     } 
2

釋放finally塊中的信號量,使其始終正確釋放。無需檢測取消。

此外,埋在日誌消息副作用都不好作風:

LogtoStatusText("..." + ASemaphore.Release()); 

我只找到這個通過文本搜索。否則就不會注意到這個錯誤。

+0

良好的漁獲物中,LogtoStatusText只是查看究竟發生了什麼事與一個輸出pp,但你完全正確 – Pseudonym

+0

另外,如果取消已被請求,我不希望現有的等待任務進入信號燈,他們將如果我不使用等待(cancellationToken) – Pseudonym

+0

我意識到這個答案可能實際上並沒有幫助你解決了這個問題。考慮使用'throw;'使得取消異常從'Factor'中冒出來。這種方式消除傳播。另外,考慮在幾個地方使用await來簡化邏輯。有些東西現在被聽到要閱讀。等待自動轉換回UI線程特別有用。 – usr

1

使用取消標記:

using System; 
using System.Threading; 
using System.Threading.Tasks; 
class Program 
{ 
    static void Main() 
    { 
     var tokenSource2 = new CancellationTokenSource(); 
     CancellationToken ct = tokenSource2.Token; 

     var task = Task.Factory.StartNew(() => 
     { 

      // Were we already canceled? 
      ct.ThrowIfCancellationRequested(); 

      bool moreToDo = true; 
      while (moreToDo) 
      { 
       // Poll on this property if you have to do 
       // other cleanup before throwing. 
       if (ct.IsCancellationRequested) 
       { 
        // Clean up here, then... 
        ct.ThrowIfCancellationRequested(); 
       } 

      } 
     }, tokenSource2.Token); // Pass same token to StartNew. 

     tokenSource2.Cancel(); 

     // Just continue on this thread, or Wait/WaitAll with try-catch: 
     try 
     { 
      task.Wait(); 
     } 
     catch (AggregateException e) 
     { 
      foreach (var v in e.InnerExceptions) 
       Console.WriteLine(e.Message + " " + v.Message); 
     } 
     finally 
     { 
      tokenSource2.Dispose(); 
     } 

     Console.ReadKey(); 
    } 
} 

https://msdn.microsoft.com/en-us/library/dd997396%28v=vs.110%29.aspx

+0

這與信號量和ContinueWith配合如何?我以前看過這篇文章,但我不認爲它解決了我在尋找的內容 – Pseudonym

相關問題