为什么 AggregateException 的 catch 块不足以处理取消?

问题描述 投票:0回答:1

运行以下代码:

using static System.Console;
WriteLine("Handling cancellations and exceptions.");
CancellationTokenSource cts = new();
CancellationToken token = cts.Token;

var transferMoney = Task<string>.Factory.StartNew(
 () =>
 {
     WriteLine($"Initiating the money transfer.");
     int progressBar = 0;
     WriteLine("Press c to cancel withing 5 sec.");
     // Assuming the task will take 5 seconds.
     // So, after every second, we'll increase the progress by 20%
     for (int i = 0; i < 5; i++)
     {
         token.ThrowIfCancellationRequested();
         Thread.Sleep(1000);
         progressBar += 20;
         WriteLine($"Progress:{progressBar}%");
     }
     return "Money transfer is completed.";
 }, token);

var input = ReadKey().KeyChar;
if (input.Equals('c'))
{
    WriteLine("\nCancellation is requested.");
    cts.Cancel();
}
try
{

    transferMoney.Wait();   
    WriteLine(transferMoney.Result);
}
catch (AggregateException ae)
{
    ae.Handle(e =>
    {
        WriteLine($"Caught error :  {e.Message}");
        return true;
    });
}

catch (OperationCanceledException oce)
{
    WriteLine($"Caught error due to cancellation :{oce.Message}");
}

WriteLine($"Payment processing status: {transferMoney.Status}");
WriteLine("Thank you, visit again!");

这是一个示例输出(预期行为 - 毫无疑问):

Handling cancellations and exceptions.
Initiating the money transfer.
Press c to cancel withing 5 sec.
Progress:20%
Progress:40%
Progress:60%
c
Cancellation is requested.
Progress:80%
Caught error :  A task was canceled.
Payment processing status: Canceled
Thank you, visit again!

现在使用以下任一语句更新 try 块:

try
{
    //transferMoney.Wait();
    transferMoney.Wait(token); 
    //await transferMoney; //Same observation
    // There is no change in the remaining code

并再次运行代码。这是一个示例输出。

Handling cancellations and exceptions.
Initiating the money transfer.
Press c to cancel withing 5 sec.
Progress:20%
Progress:40%
c
Cancellation is requested.
**Caught error due to cancellation :The operation was canceled.**
Payment processing status: Running
Thank you, visit again!

请注意,这次 AggregateException 的 catch 块不足以处理 OperationCanceledException。我想知道这背后的原因。你能分享一下你的想法吗——我错过了什么? [补充说明:我知道在之前的情况下, wait() 只能抛出 AggregateException 但这个重载版本的 Wait(即 Wait(token))也可以抛出 OperationCancelledException 。 ]

task task-parallel-library cancellation cancellation-token c#-12.0
1个回答
0
投票

来自

Task.Wait(CancellationToken)
文档:

但是一旦取消标记被取消,等待就会被取消,并抛出

OperationCanceledException

还有

Wait(CancellationToken)
方法创建一个可取消的等待;也就是说,它会导致当前线程等待,直到发生以下情况之一:

  • 任务完成。
  • 取消令牌被取消。在这种情况下,调用
    Wait(CancellationToken)
    方法会抛出
    OperationCanceledException

在第二种情况下,您的等待被取消,因此您得到

OperationCanceledException
,即该方法可以理解(受监视令牌的)取消已经发生,而不是一些随机错误(这将进入“任务完成”情况并导致在
AggregateException
).

OperationCanceledException
不继承自
AggregateException
,因此
catch(AggregateException)
无法处理它。

如果任务执行期间发生任何异常,则

OperationCanceledException
的优先级高于任何其他异常。来自
Task.Wait
源代码

// If cancellation was requested and the task was canceled, throw an
// OperationCanceledException.  This is prioritized ahead of the ThrowIfExceptional
// call to bring more determinism to cases where the same token is used to
// cancel the Wait and to cancel the Task.  Otherwise, there's a race condition between
// whether the Wait or the Task observes the cancellation request first,
// and different exceptions result from the different cases.
if (IsCanceled) cancellationToken.ThrowIfCancellationRequested();

// If an exception occurred, or the task was cancelled, throw an exception.
ThrowIfExceptional(true);
© www.soinside.com 2019 - 2024. All rights reserved.