取消任務

>>返回《C# 併發編程》html

CancellationToken.None 是一個等同於默認的特殊值,表示這個方法是永遠不會被取消的。編程

實例代碼併發

static async Task CancelableMethodAsync(CancellationToken token)
{
    await Task.Delay(1000, token);
    throw new ArgumentException();
}
public static async Task IssueCancelRequestAsync()
{
    var cts = new CancellationTokenSource();
    var task = CancelableMethodAsync(cts.Token);
    // 這裏,操做在正常運行。
    // 發出取消請求。
    cts.Cancel();
    //(異步地)等待操做結束。
    try
    {
        await task;
        // 如運行到這裏,說明在取消請求生效前,操做正常完成 。
    }
    catch (OperationCanceledException ex)
    {
        // 如運行到這裏,說明操做在完成前被取消。
        System.Console.WriteLine(ex.GetType().Name);
    }
    catch (Exception ex)
    {
        // 如運行到這裏,說明在取消請求生效前,操做出錯並結束。
        System.Console.WriteLine(ex.GetType().Name);
    }
}

輸出:異步

TaskCanceledException

1. 取消請求

public static int CancelableMethod(CancellationToken cancellationToken)
{
    for (int i = 0; i != 100000; ++i)
    {
        // cancellationToken.WaitHandle.WaitOne(1000);
        Thread.Sleep(1);
        // 這裏作一些計算工做。
        if (i % 1000 == 0)
            cancellationToken.ThrowIfCancellationRequested();
    }
    return 42;
}

2. 超時後取消

public static async Task IssueTimeoutAsync()
{
    Stopwatch sw = Stopwatch.StartNew();
    try
    {
        var cts = new CancellationTokenSource();
        var token = cts.Token;
        cts.CancelAfter(TimeSpan.FromSeconds(2));
        await Task.Delay(TimeSpan.FromSeconds(4), token);
    }
    finally
    {
        System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
    }
}

輸出:async

2004ms
Unhandled Exception: ... ...

只要執行代碼時用到了超時,就該使用 CancellationTokenSourceCancelAfter (或者用構造函數)。雖然還有其餘途徑可實現這個功能,可是使用現有的取消體系是最簡單也是最高效的。函數

3. 取消並行

public class Matrix
{
    public void Rotate(float degrees) { }
}

//只作展現
public static void RotateMatrices(IEnumerable<Matrix> matrices, float degrees, CancellationToken token)
{
    Parallel.ForEach(matrices, new ParallelOptions
    {
        CancellationToken = token
    },
    matrix => matrix.Rotate(degrees));
}

4. 取消響應式代碼

注入取消請求url

  • 某一個層次的代碼須要響應取消請求,同時它自己也要向下一層代碼發出取消請求(但不會向上傳遞)。
public static async Task RunGetWithTimeoutAsync()
{
    CancellationTokenSource source = new CancellationTokenSource();
    await GetWithTimeoutAsync("http://www.baidu.com", source.Token);
}

public static async Task<HttpResponseMessage> GetWithTimeoutAsync(string url, CancellationToken cancellationToken)
{
    var client = new HttpClient();
    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        cts.CancelAfter(TimeSpan.FromMilliseconds(100));
        var combinedToken = cts.Token;
        return await client.GetAsync(url, combinedToken);
    }
}

輸出:pwa

Unhandled Exception: Unhandled exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AggregateException: One or more errors occurred. (A task was canceled.) ---> System.Threading.Tasks.TaskCanceledException ... ...code

5. 與其餘取消體系的互操做

有一些外部的或之前遺留下來的代碼採用了非標準的取消模式。如今要用標準的CancellationToken 來控制這些代碼htm

public static async Task RunPingAsync()
{
    var cts = new CancellationTokenSource();
    var task = PingAsync("192.168.0.101", cts.Token);
    //cts.Cancel();
    await task;
}
public static async Task<PingReply> PingAsync(string hostNameOrAddress, CancellationToken cancellationToken)
{
    Stopwatch sw = Stopwatch.StartNew();
    try
    {
        var ping = new Ping();
        using (cancellationToken.Register(() => ping.SendAsyncCancel()))
        {
            return await ping.SendPingAsync(hostNameOrAddress);
        }
    }
    finally
    {
        System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
    }
}

注意: 爲了避免內存和資源的泄漏,一旦再也不須要使用回調函數了,就要釋放這個回調函數註冊。

相關文章
相關標籤/搜索