2017-08-09 311 views
0

我想在一定的毫秒後取消耗時的任務,對於我的情況,我認爲CancellationToken.Register方法最適合與其他方法進行持續輪詢或WaitHandle相比。 CancellationToken.Register方法將幫助我定義一個委託,在該委託中計劃將任務取消並停止任務的執行;這個委託將在任務被取消時被調用(根據我的目標某個毫秒後)。下面是測試代碼,我有我打算擴大爲多個任務,後來與嵌套任務:正確處理CancellationToken.Register的異常

List<Task> tasks = new List<Task>(); 
CancellationTokenSource tokenSource = new CancellationTokenSource(); 
CancellationToken cancellationToken = tokenSource.Token; 

Task t1 = Task.Factory.StartNew(() => 
{ 
    // check cancellation token before task begins 
    if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); 

    // register a callback to handle cancellation token anytime it occurs 
    cancellationToken.Register(() => 
    { 
     Console.WriteLine("Task t1 cancelled"); 
     cancellationToken.ThrowIfCancellationRequested(); 
    }); 

    // simulating a massive task; note, it is not a repeating task 
    Thread.Sleep(12000); 
}, cancellationToken); 

tasks.Add(t1); 

try 
{ 
    // cancel token after 3000 ms + wait for all tasks 
    tokenSource.CancelAfter(3000); 
    Task.WaitAll(tasks.ToArray()); 

    // OR wait for all tasks for 3000 ms and then cancel token immediately 
    //Task.WaitAll(tasks.ToArray(), 3000); 
    //tokenSource.Cancel(); 
} 
catch (AggregateException e) 
{ 
    Console.WriteLine("\nAggregateException thrown with the following inner exceptions:"); 
    // Display information about each exception. 
    foreach (var v in e.InnerExceptions) 
    { 
     if (v is TaskCanceledException) 
       Console.WriteLine(" TaskCanceledException: Task {0}",            ((TaskCanceledException)v).Task.Id); 
     else 
       Console.WriteLine(" Exception: {0}", v.GetType().Name); 
    } 
    Console.WriteLine(); 
} 
finally 
{ 
    tokenSource.Dispose(); 
} 

我,不過,裏面cancellationToken.Register回調方法的執行過程中面臨着異常處理的問題。對cancellationToken.ThrowIfCancellationRequested()的調用給我例外:「OperationCanceledException未被用戶代碼處理」,後面跟着「AggregateException was unhandled」。我已經閱讀了VS設置,取消了第一個OperationCanceledException異常的User-unhandled異常,但是我的應用程序在第二個AggregateException異常之後終止; Task.WaitAll的try..catch塊似乎沒有處理這個問題。

我試圖在一個try..catch塊中放置cancellationToken.ThrowIfCancellationRequested(),但這種方法的問題是該任務繼續執行剩餘的步驟,我不希望這樣做。我沒有看到這種輪詢方式的行爲。

// poll continuously to check for cancellation instead of Register 
// but I do not want my massive task inside this repeating block 
while (true) 
{ 
    if (cancellationToken.IsCancellationRequested) 
    { 
     Console.WriteLine("Task t1 Canceled."); 
     cancellationToken.ThrowIfCancellationRequested(); 
    } 
} 

我在做什麼錯誤的CancellationToken.Register方法?

回答

0

您看到的錯誤完全是因爲您沒有在try-catch塊內包裝ThrowIfCancellationRequested。

在我看來,這取決於你在做什麼來代替睡眠()。 結束以合作方式的任務像

while(!cancellationToken.IsCancellationRequested) 
{ 
    // Do stuff 

    // Also check before doing something that may block or take a while 
    if(!cancellationToken.IsCancellationRequested) 
    { 
     Stream.Read(buffer, 0, n); 
    } 
} 

應該停止它的最好辦法。 如果你真的需要停止它,不管是什麼,我會做包裝在另一個任務

Task.Run(() => 
{ 
    // Simulate a long running task 
    Thread.Sleep(12*1000); 
}, cancellationToken); 

(測試工作) 這樣你不會看到任何異常未處理的異常。 此外,你可能想看看這個:How do I abort/cancel TPL Tasks?