可用來 控制多執行序停止和中斷和Cancel後執行的callback function
建立類別的三種多載Construct
第二種跟第三種多載的建構子參數是設定 多久後會取消Task, 單位為 millisecond(毫秒) 與 TimeSpan
IsCancellationRequested 會在delay時間過後為true,並執行Token中Register的Action。 (Token的Register 可以註冊 工作取消後會執行的動作)
public CancellationTokenSource();
public CancellationTokenSource(int millisecondsDelay);
public CancellationTokenSource(TimeSpan delay);
判斷該 CancellationTokenSource 是否被取消的Property IsCancellationRequested
當被Cancel掉時,該值為true
public bool IsCancellationRequested { get; }
建立具有監聽用途的CancellationTokenSource
該CancellationTokenSource的狀態是否被Cancel 是由 傳入的CancellationToken的狀態來決定,當傳入的任一CancellationToken被Cancel掉時,該CancellationTokenSource也同樣被Cancel掉。
public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2);
public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens);
/// <summary>
/// 建立有連結的CancellationTokenSource
/// 當用來建立的任一CancellationTokenSource被Cancel掉時,該LinkedTokenSource也同樣被Cancel掉
///
/// 測試呼叫 CreateLinkedTokenSource
/// </summary>
public static void CreateLinkedTokenSource()
{
    CancellationTokenSource notCancellationTokenSource = new CancellationTokenSource();
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    CancellationTokenSource test = CancellationTokenSource.CreateLinkedTokenSource(notCancellationTokenSource.Token, cancellationTokenSource.Token);
    cancellationTokenSource.Cancel();
    Console.WriteLine($"notCancellationTokenSource類別是否被Cancel了 = {notCancellationTokenSource.IsCancellationRequested}");
    Console.WriteLine($"cancellationTokenSource類別是否被Cancel了 = {cancellationTokenSource.IsCancellationRequested}");
    Console.WriteLine($"該CancellationTokenSource類別是否被Cancel了 = {test.IsCancellationRequested}");
}

Cancel 掉 CancellationTokenSource
當CancellationTokenSource被Cancel掉時,已經執行中的Task並不會中斷
必須要自行在委派中判斷CancellationTokenSource.IsCancellationRequested or CancellationTokenSource.Token.IsCancellationRequested 是否為true
再處理中斷Task後的動作。
public void Cancel();
public void Cancel(bool throwOnFirstException);
/// <summary>
/// cancel掉Task
///
/// 測試呼叫 Cancel()
/// </summary>
public static void Cancel()
{
    stopwatch.Stop();
    stopwatch.Reset();
    stopwatch.Start();
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    CancellationToken a = new CancellationToken();
    Task.Run(() =>
    {
        while (true)
        {
            Thread.Sleep(500);
            if (cancellationTokenSource.Token.IsCancellationRequested)
            {
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task have canceled!{cancellationTokenSource.IsCancellationRequested}");
                return;
            }
            else
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Running!{cancellationTokenSource.IsCancellationRequested}");
        }
    }, cancellationTokenSource.Token);
    Thread.Sleep(5000);
    cancellationTokenSource.Cancel();
}

/// <summary>
/// 透過cancellationTokenSource設定Task被cancel掉所要執行的delegate
///
/// 測試呼叫 Cancel() & Cancel(bool throwOnFirstException)
/// 呼叫 Cancel() 會執行 Cancel(false)
/// 該boolean參數 用來判斷當有Register被Cancel掉要執行的動作噴Exception時
/// 若為是,代表直接噴Exception,不管其餘動作是否執行完畢。
/// 若為否,則會執行完其餘Register的動作之後才會噴Exception。
/// </summary>
public static void Cancel_Register()
{
    stopwatch.Stop();
    stopwatch.Reset();
    stopwatch.Start();
    try
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        // 註冊的動作是LIFO,最晚註冊的開始執行
        cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Canceled1"));
        cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Canceled2"));
        cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Canceled3"));
        cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Canceled4"));
        cancellationTokenSource.Token.Register(() => throw new Exception($"{stopwatch.ElapsedMilliseconds} ms Task Canceled"));
        cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Canceled5"));
        Task.Run(() =>
        {
            while (true)
            {
                Thread.Sleep(500);
                if (!cancellationTokenSource.IsCancellationRequested)
                    Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Running!");
            }
        }, cancellationTokenSource.Token);
        Thread.Sleep(5000);
        Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms 執行 Task Cancel(false)");
        cancellationTokenSource.Cancel();
        //cancellationTokenSource.Cancel(true);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"發生異常, Error:{ex.Message}");
    }
}
執行 Cancel() 也等於 Cancel(false),當Cancel後執行的Register有異常時會執行完所有Register
執行 Cancel(true),當Cancel後執行的Register有發生異常時,其餘的Register則不會執行
定時 Cancel 掉 CancellationTokenSource
有定時功能的Cancel
public void CancelAfter(int millisecondsDelay);
public void CancelAfter(TimeSpan delay);
/// <summary>
/// 透過cancellationTokenSource設定Task幾秒後會cancel掉
///
/// 測試呼叫 CancelAfter(int millisecondsDelay) & CancelAfter(TimeSpan delay)
/// millisecondsDelay 與 delay,代表多久要取消該Task
/// </summary>
public static void CancelAfter()
{
    stopwatch.Stop();
    stopwatch.Reset();
    stopwatch.Start();
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    Task.Run(() =>
    {
        while (true)
        {
            Thread.Sleep(500);
            if (!cancellationTokenSource.IsCancellationRequested)
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Running!");
            else
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task have canceled!");
        }
    }, cancellationTokenSource.Token);
    //cancellationTokenSource.CancelAfter(3000);
    cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5));
}

Dispose CancellationTokenSource
當CancellationTokenSource被Dispose掉,則設定的Register和Cancel Timer都被清除
且只剩IsCancellationRequested可使用,若繼續呼叫Cancel() 則會噴Exception
public void Dispose();
/// <summary>
/// 測試呼叫 Dispose
/// 當CancellationTokenSource被Dispose掉,代表先前設定的Register和Cancel Timer都被清除
/// </summary>
public static void Dispose()
{
    stopwatch.Stop();
    stopwatch.Reset();
    stopwatch.Start();
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    Task.Run(() =>
    {
        while (true)
        {
            Thread.Sleep(500);
            if (!cancellationTokenSource.IsCancellationRequested)
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Running!");
            else
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task have canceled!");
        }
    }, cancellationTokenSource.Token);
    cancellationTokenSource.Token.Register(() => Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms Task Register"));
    // 5秒後Cancel掉Task
    cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5));
    // 3秒後dispose掉CancellationTokenSource,清除掉CancelAfter的timer和Register動作
    Thread.Sleep(3000);
    cancellationTokenSource.Dispose();
    // 被dispose掉後除了IsCancellationRequested之外  其餘方法無法再調用,會噴Exception
    //cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(2));
}
程式碼 可參考 w4560000/CSharp_Practice/CancellationTokenSourceAPI
轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡


