运行以下代码:

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.我想知道这背后的原因.你能分享你的 idea 吗--我错过了哪里? [补充说明:我知道在前面的例子中,Wait()只能抛出AggregateException,但这个重载版本的Wait(即Wait(Token))也会抛出OperationCancelledException.]

推荐答案

Task.Wait(CancellationToken)份文档中选出:

但是,一旦取消令牌并抛出OperationCanceledException,等待就被取消.

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

  • 任务完成.
  • 取消令牌被取消.在本例中,对Wait(CancellationToken)方法的调用抛出一个OperationCanceledException.

在第二种情况下,您的等待被取消,因此您得到OperationCanceledException,即该方法可以理解取消(监视令牌)已经发生,而不是某个随机错误(这将转到"任务完成"情况,并将导致AggregateException).

OperationCanceledException不是从AggregateException继承的,所以catch(AggregateException)无法处理它.

OperationCanceledException优先于任何其他异常(如果在任务执行期间发生任何异常).从Task.Wait source code人开始:

// 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);

Csharp相关问答推荐

在Microsoft XNA框架(MonoGame)中旋转相机

为什么使用DXGI输出复制和Direct 3D时捕获的图像数据全为零?

IComponition.获取IReadOnlyCollection的返回默认属性值

有没有办法把+02:00转换成TimeSpan?""

在C#WinUI中,一个关于System的崩溃."由于未知原因导致执行不例外"

实体核心框架--HasColumnType和HasPrecision有什么不同?

Azure DEVOPS找不到定制的Nuget包

Take()方法如何与IAsyncEnumerable一起使用

使用可信第三方的Iext8.Net pdf签名

Unix上的.NET(核心):.NET意外地未看到通过P/Invoke系统调用对环境变量进行的进程内修改

异步任务导致内存泄漏

.NET并发词典交换值

EF核心区分大小写的主键

在C#.NET项目中启动时,如何等待提升的PowerShell进程退出?

为什么C#/MSBuild会自发地为不同的项目使用不同的输出路径?

是否可以在Entity Framework Core中使用只读 struct 作为拥有实体?

删除MudRadio时,MudRadioGroup未 Select 正确的MudRadio

如何更改Datagridview行标题

从具有泛型类型约束的类继承-Blazor

C#Web服务转换为 node /Express不工作