2

請如果有人能幫助我。 我正在嘗試使用TPL鏈接的取消標記。問題是取消主CancellationTokenSource後,鏈接標記屬性IsCancellationRequested的值仍爲「false」。C#任務 - 鏈接的取消令牌不起作用

我開始了兩項任務,只是要確定 - 但它應該是同樣的事情。第一次傳遞CancellationToken,第二次傳遞CancellationTokenSource。行爲是一樣的:在while循環中 - 條件linkedToken.IsCancellationRequested在取消後保持「false」。

這裏是我使用的代碼:

public class Manager 
{ 
    private Task tokenTask; 
    private Task sourceTask; 
    private CancellationTokenSource mainCancelationTokenSource; 
    private CancellationToken mainToken; 

    public Manager() 
    { 
     this.mainCancelationTokenSource = new CancellationTokenSource(); 
     this.mainToken = mainCancelationTokenSource.Token; 
     this.mainToken.Register(MainCanceled); 
    } 

    public void Start() 
    { 
     Workers w = new Workers(); 
     tokenTask = Task.Run(() => w.DoWorkToken(mainToken), mainToken); 
     sourceTask = Task.Run(() => w.DoWorkSource(mainCancelationTokenSource), mainCancelationTokenSource.Token); 

    } 
    public void Cancel() 
    { 
     mainCancelationTokenSource.Cancel(); 
    } 

    private void MainCanceled() 
    { 
     try 
     { 
      tokenTask.Wait(); 
     } 
     catch (Exception e) 
     { 

     } 

     try 
     { 
      sourceTask.Wait(); 
     } 
     catch (Exception e) 
     { 

     } 
    } 
} 

class Workers 
{ 
    public void DoWorkToken(CancellationToken mainToken) 
    { 
     CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(mainToken); 
     CancellationToken linkedToken = linkedCts.Token; 

     while (!linkedToken.IsCancellationRequested) 
     { 
      Random r = new Random(); 
      Task.Delay(200 * r.Next(1, 11)).Wait(); 
     } 

     linkedToken.ThrowIfCancellationRequested(); 
    } 

    public void DoWorkSource(CancellationTokenSource mainCts) 
    { 
     CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(mainCts.Token); 

     while (!linkedCts.Token.IsCancellationRequested) 
     { 
      Random r = new Random(); 
      Task.Delay(200 * r.Next(1, 11)).Wait(); 
     } 

     linkedCts.Token.ThrowIfCancellationRequested(); 
    } 
} 

從一個控制檯應用程序的主要方法啓動此代碼:

class Program 
{ 
    static void Main(string[] args) 
    { 
      Manager manager = new Manager(); 
      manager.Start(); 
      //Console.ReadKey(); 

      Thread.Sleep(5000); 
      manager.Cancel(); 
    } 
} 

謝謝您的幫助!

回答

5

這個問題的根源是這一行:

this.mainToken.Register(MainCanceled); 

您註冊一個回調時,取消標記來執行。內部回調被委託給父母CancellationTokenSource,並被放入一些回調列表中,以便在請求源取消時執行。在那個處理程序中,你做

tokenTask.Wait(); 
sourceTask.Wait(); 

所以這個回調將不會完成,直到相關的任務完成。 在任務完成此

while (!linkedToken.IsCancellationRequested) { 
    // loop here 
} 

那麼任務將不會被取消之前要求完成。

現在到了棘手的部分:當您創建鏈接的標記源

CancellationTokenSource.CreateLinkedTokenSource(mainToken) 

它將放回調在父(mainToken)令牌源的列表。當執行此回調時,鏈接的令牌源也會被取消。此回調將在您回撥列表中的後

所以你死鎖,因爲第一個回調(你的)等待任務完成,任務等待鏈接的令牌IsCancellationRequested等於true,並且鏈接的令牌源等待它自己的回調來完成設置該標誌。

爲了解決這個問題,只是刪除回調,或者它的方式寫,它不會阻止等待相關任務來完成,或者更改任務不是鏈接令牌源IsCancellationRequested標誌等。

+0

謝謝你的回答。當我刪除回調,一切正常。但只是要清楚,我已經改變了條件,而while循環爲:while(!mainToken.IsCancellationRequested)'。 (這是來源被取消的令牌 - 在另一個while循環中,我分別對源進行條件處理)。我已將回調加回。這個令牌被取消,任務結束,回調中的Wait()結束並且一切正常。我可以得出結論:鏈接源被放到這個「回調」列表中,但源本身被取消,儘管註冊回調? – Thomas

+0

@Thomas你是對的,我的部分錯誤,我已經更新了我的答案。 – Evk

+0

再次感謝您的回答。 – Thomas