.Net 並行編程系列之 Task取消

来源:https://www.cnblogs.com/zhengzc/archive/2018/03/05/8508889.html
-Advertisement-
Play Games

在Task運行過程中,我們可以通過.Net 4中的內置方法來取消Task的運行。 創建一個可取消的Task需要用到下麵的一些對象: 1.System.Threading.CancellationTokenSource實例 2.通過CancellationTokenSource.Token屬性獲得一個 ...


在Task運行過程中,我們可以通過.Net 4中的內置方法來取消Task的運行。

創建一個可取消的Task需要用到下麵的一些對象:

1.System.Threading.CancellationTokenSource實例

CancellationTokenSource tokenSource = new CancellationTokenSource();

2.通過CancellationTokenSource.Token屬性獲得一個取消令牌

CancellationToken token = tokenSource.Token;

3.創建Task對象,並且在構造函數傳入Action(或者Action<T>)委托作為第一個參數,CancellationToken作為第二個參數(重要)

Task task = new Task(() =>
{
// do something
}, token);
task.Start();

 4.創建Task對象也可以通過調用System.Threading.Tasks.TaskFactory 類提供的靜態方法

Task task = Task.Factory.StartNew(() =>
{
    // do something ......
}, token);

 

如果想要取消Task的運行,除了要調用CancellationTokenSource實例的Cancel()方法之外,我們的Action委托中還需要檢測CancellationToken的取消狀態並編寫相應代碼(拋出異常)來阻止Task的運行。

可以通過以下方式來檢測Task取消狀態:

1.通過輪詢的方式檢測CancellationToken取消標記,該操作類似於輪詢非同步操作的IAsyncResult.IsCompleted狀態,也是通過在迴圈中判斷CancellationToken.IsCancellationRequested屬性來檢測Task是否被取消,如果為True則在Action委托中拋出異常來取消繼續運行Task。 

static void Main(string[] args)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    Task task = new Task(() =>
    {
        while (true)
        {
            if(token.IsCancellationRequested)
            {
                // 釋放資源操作等等...
                throw new OperationCanceledException(token);
            }
            Console.Write(".");
            Thread.Sleep(100);
        }
    }, token);

    Console.WriteLine("Task is Running.");
    Console.WriteLine("Press anykey to cancel task.");    
task.Start();
Console.ReadKey(
true); Console.WriteLine(); Console.WriteLine("Cancelling task."); tokenSource.Cancel();
Console.WriteLine(
"Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }

 

如果不需要釋放系統資源,那麼可以直接調用CancellationToken.ThrowIfCancellationRequested()方法,其實現如下:

[__DynamicallyInvokable]
public void ThrowIfCancellationRequested()
{
    if (this.IsCancellationRequested)
    {
        this.ThrowOperationCanceledException();
    }
}

 示例:

while (true)
{
    token.ThrowIfCancellationRequested();
    Console.Write(".");
    Thread.Sleep(100);
}

 

2.通過委托(Delegate)來檢測Task是否取消,註冊一個在取消CancellationToken時調用的委托,當CancellationTokenSource發送取消請求時,該委托即會運行,我們可以在委托方法中實現通知功能等等。

static void Main(string[] args)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    Task task = new Task(() =>
    {
        while (true)
        {
            token.ThrowIfCancellationRequested();
            Console.Write(".");
            Thread.Sleep(100);
        }
    }, token);

    token.Register(() => { Console.WriteLine("The delegate is triggered."); });

    Console.WriteLine("Task is Running.");
    Console.WriteLine("Press anykey to cancel task.");

    task.Start();

    Console.ReadKey(true);
    Console.WriteLine();
    Console.WriteLine("Cancelling task.");
    tokenSource.Cancel();

    Console.WriteLine("Main method complete.");
    Console.WriteLine("Press enter to finish.");
    Console.ReadLine();
}

 

3.用WaitHandle來檢測Task是否取消,當在CancellationToken.WaitHandle上調用WaitOne()方法時,會阻止當前線程執行,直到該CancellationToken接收到取消請求標記時,被token阻止的Task線程才會釋放並繼續執行。

多個Task實例使用同一個CancellationToken時,當CancellationToken接收到取消請求標記時,所有在構造函數中使用該token實例化的Task都會被取消。WaitOne(int millisecondsTimeout)可以使用等待毫秒數作為參數,超過等待時間將會釋放阻止的線程。

不管WaitOne(int millisecondsTimeout)設置多長的等待時間,只要CancellationToken接收到取消請求標記時Task都會取消,而如果使用Thread.Sleep(100000)進行線程等待時,那麼即使CancellationToken接收到取消請求標記,該Task也會等到Thread.Sleep執行完成才會Cancel。

static void Main(string[] args)
{
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;

    Task task = new Task(() =>
    {
        while (true)
        {                    
            token.ThrowIfCancellationRequested();
            Console.Write(".");
            Thread.Sleep(100);
        }
    }, token);

    Task task1 = new Task(() =>
    {
        token.WaitHandle.WaitOne();
        Console.WriteLine("WaitHandle released.");
    }, token);

    Console.WriteLine("Task is Running.");
    Console.WriteLine("Press anykey to cancel task.");

    task.Start();
    task1.Start();

    Console.ReadKey(true);
    Console.WriteLine();
    Console.WriteLine("Cancelling task.");
    tokenSource.Cancel();
Console.WriteLine(
"Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }

 

4.通過Task的IsCancelled屬性來判斷Task是否被取消,如果Task實例化時構造函數沒有傳入CancellationToken對象,則取消Task運行之後通過Task.IsCanceled屬性獲取到的值還是False而不是TrueTask.ContinueWith()方法如果沒有傳入CancellationToken對象,則Task即使是取消執行也會繼續執行Task.ContinueWith()方法的Action委托,如果傳入與Task相同的CancellationToken對象,則Task取消執行後Task.ContinueWith()方法中的Action委托也不會繼續執行。

{
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    Task task = new Task(() =>
    {
        try
        {
            Console.WriteLine("Task: Running");
            Thread.Sleep(5000);
            Console.WriteLine("Task: ThrowIfCancellationRequested");
            token.ThrowIfCancellationRequested();
            Thread.Sleep(2000);
            Console.WriteLine("Task: Completed");
        }
        catch (Exception exception)
        {
            Console.WriteLine("Task: " + exception.GetType().Name);
            throw;
        }
    }, token);

    task.ContinueWith(t => Console.WriteLine("ContinueWith: tokenSource.IsCancellationRequested = {0}, task.IsCanceled = {1}, task.Exception = {2}", tokenSource.IsCancellationRequested, t.IsCanceled, t.Exception == null ? "null" : t.Exception.GetType().Name));
    task.Start();
    
    Thread.Sleep(1000);

    Console.WriteLine("Main: Cancel");
    tokenSource.Cancel();

    try
    {
        Console.WriteLine("Main: Wait");
        task.Wait();
    }
    catch (Exception exception)
    {
        Console.WriteLine("Main: Catch " + exception.GetType().Name);
    }

    Console.WriteLine("Main: task.IsCanceled = {0}", task.IsCanceled);
    Console.WriteLine("Press any key to exit...");

    Console.ReadKey(true);
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這題有如下幾個點要註意: 1.最開始輸出的開始時間和截止時間,這裡是不包含截止時間的。 2.月份和星期的英文表示是大小寫任意的,並未規定必須是Sat這種形式。 3.星期天的數字標識是0。 我的思路是,首先將月份、天數、小時、分鐘、星期統統規格化,格式如下: 月份:規格化前:1,2-4 規格化後:1 ...
  • 一、發現問題 <select id="queryStudentByNum" resultType="student" parameterType="string"> select num,name,phone from student <where> <if test = " num!=null a ...
  • (一) 前言 簡單的說就是分為2層,頁面class 和測試class。 頁面class:分為父類和子類(子類指具體的頁面,每一個頁面都創建一個類),父類中定義公有的屬性和方法(操作)。 #對面向對象有瞭解的,應該很容易理解抽象出公有屬性和方法的意思 #父類和子類我是按自己的理解進行描述的,或者可以說 ...
  • 通過pycharm安裝selenium 1.配置好python和pycharm,打開pycharm,點擊左上角的File->Setting->Project Interpreter,點擊右側的添加按鈕 2.在新彈出的視窗中輸入selenium,選擇selenium,點擊左下角的install pac ...
  • #include #include #include using namespace std; const int MAX=1005; int main() { int x[MAX]={0},y[MAX]={0},z[2*MAX+1]={0}; string a,b; cin>>a>>b; reve... ...
  • 棧 1.定義:棧是限定僅在表尾進行插入或刪除操作的線性表。因此,對棧來說,表尾端有其特殊含義,稱為棧頂,相應地, 表頭端稱為棧底。不含元素的空表稱為空棧。 假設棧S=(a1,a2,a3,...,an),則稱a1為棧底元素,an為棧頂元素。棧中元素按a1,a2,a3,...,an的次序進棧,退棧的第 ...
  • 一、反射 1、反射概念 JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 要想解剖一個類,必須先要獲取到該類的位元組碼文件對象。而解剖使用的就 ...
  • 1. 方法思路: 使用數據字典【Dictionary<string, string>】,聲明一個list集合,將“XML子節點名稱”、“節點值”以鍵【節點名稱】值【節點值】對的形式存入此集合,然後將此集合作為參數傳入封裝的公共方法中即可; 2. 公共方法: 3. 對公共方法的調用: 4. 整體的完整 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...