簡體   English   中英

等待取消的任務完成,然后再繼續執行

[英]Wait for a canceled task to complete before continuing execution

我有以下代碼,可以取消Task ,但是在將OperationCanceledException拋出給調用者之前,我基本上需要等待它完成(以確保完整性)。

public static void TaskCancellationTest() {
    try {
        Console.WriteLine("TaskCancellationTest started.");
        var cts = new CancellationTokenSource();
        var t = Task.Run(() => {
            if (cts.Token.IsCancellationRequested) return;
            Console.WriteLine("1");
            Task.Delay(2000).Wait();
            Console.WriteLine("2");
        }).ContinueWith(task => {
            if (cts.Token.IsCancellationRequested) return;
            Console.WriteLine("3");
            Task.Delay(2000).Wait();
            Console.WriteLine("4");
        });
        Task.Run(() => {
            Task.Delay(1000).Wait();
            Console.WriteLine("Cancelling...");
            cts.Cancel();
        });
        t.Wait();
        try {
            cts.Token.ThrowIfCancellationRequested();
        }
        catch (OperationCanceledException) {
            Console.WriteLine("Gracefully canceled.");
        }
        Console.WriteLine("TaskCancellationTest completed.");
    }
    catch (Exception ex) {
        Console.WriteLine("TaskCancellationTest... Failure: " + ex);
    }
}

預期的結果是:

1
Cancelling...
2
Gracefully canceled.

它可以工作,但是我更願意將CancellationToken傳遞給方法,因為我知道這是一個更好的模式。 我還希望能夠觀察方法體內的標記並調用ThrowIfCancellationRequested()中止,而不必等待下一個ContinueWith( )。

我正在使用下面的替代代碼,該代碼也有效,但是有什么方法可以引發OperationCanceledException而不是AggregateException

如果我將WaitAll()傳遞給WaitAll()方法,則問題在於它將在取消令牌后立即拋出OperationCanceledException ,而不是等待任務t1t2實際完成(它們將繼續在后台運行),並且然后只拋出異常。

public static void TaskCancellationTest2() {
    try {
        Console.WriteLine("TaskCancellationTest2 started.");
        var cts = new CancellationTokenSource();
        var t1 = Task.Run(() => {
            Console.WriteLine("1");
            Task.Delay(2000).Wait();
            Console.WriteLine("2");
        }, cts.Token);
        var t2 = t1.ContinueWith(task => {
            Console.WriteLine("3");
            Task.Delay(2000).Wait();
            cts.Token.ThrowIfCancellationRequested();
            Console.WriteLine("4");
        }, cts.Token);
        Task.Run(() => {
            Task.Delay(1000).Wait();
            Console.WriteLine("Cancelling...");
            cts.Cancel();
        });
        try {
            try {
                Task.WaitAll(t1, t2);
            }
            catch (AggregateException ae) {
                if (ae.InnerExceptions.Count == 1 && ae.InnerExceptions.Single() is OperationCanceledException) {
                    throw ae.InnerExceptions.Single();
                }
                throw;
            }
        }
        catch (OperationCanceledException) {
            Console.WriteLine("Gracefully canceled.");
        }
        Console.WriteLine("TaskCancellationTest2 completed.");
    }
    catch (Exception ex) {
        Console.WriteLine("TaskCancellationTest2... Failure: " + ex);
    }
}

在這里准備了一個小提琴。

這個問題的標題與我的非常相似,但是不幸的是,所接受的答案與我的情況無關。

您是否知道有什么方法可以實現我所希望的,那就是盡可能充分地使用CancellationToken

我認為,如果設置了CancellationToken則TPL旨在急於完成任務。 您看到此行為的部分原因是因為您正在調用t.Wait(cts.Token) 即使設置了令牌,帶CancellationToken的重載也將停止等待,即使任務尚未完成也是如此。

如果您傳入CancellationToken ,則ContinueWith也是一樣,只要設置了該令牌,任務就可以完成。

更改代碼以調用t.Wait()ContinueWith而不使用令牌,您將獲得所需的行為。

    public static void TaskCancellationTestNotWorking1()
    {
        try
        {
            Console.WriteLine("TaskCancellationTestNotWorking started.");
            var cts = new CancellationTokenSource();
            var t = Task.Run(() =>
            {
                Console.WriteLine("1");
                Thread.Sleep(2000);
                Console.WriteLine("2");
            }, cts.Token).ContinueWith(task =>
            {
                Console.WriteLine("3");
                Thread.Sleep(2000);
                cts.Token.ThrowIfCancellationRequested();
                Console.WriteLine("4");
            });

            Task.Run(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine("Cancelling...");
                cts.Cancel();
            }, cts.Token);

            try
            {
                t.Wait();
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("IsCanceled " + t.IsCanceled);
                Console.WriteLine("IsCompleted " + t.IsCompleted);                    

                Console.WriteLine("Gracefully canceled.");
            }
            catch (AggregateException)
            {
                Console.WriteLine("IsCanceled " + t.IsCanceled);
                Console.WriteLine("IsCompleted " + t.IsCompleted);

                Console.WriteLine("Gracefully canceled 1.");
            }

            Console.WriteLine("TaskCancellationTestNotWorking completed.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("TaskCancellationTestNotWorking... Failure: " + ex);
        }
    }

您可能會覺得本文很有用。 如何取消不可取消的異步操作?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM