C# 任务。发生OperationCanceledException时等待意外行为

C# 任务。发生OperationCanceledException时等待意外行为,c#,.net,task-parallel-library,wait,cancellation,C#,.net,Task Parallel Library,Wait,Cancellation,考虑以下代码: CancellationTokenSource cts0 = new CancellationTokenSource(), cts1 = new CancellationTokenSource(); try { var task = Task.Run(() => { throw new OperationCanceledException("123", cts0.Token); }, cts1.Token); task.Wait(); } catch (Ag

考虑以下代码:

CancellationTokenSource cts0 = new CancellationTokenSource(), cts1 = new CancellationTokenSource();
try
{
    var task = Task.Run(() => { throw new OperationCanceledException("123", cts0.Token); }, cts1.Token);
    task.Wait();
}
catch (AggregateException ae) { Console.WriteLine(ae.InnerException); }
由于任务的令牌与异常的令牌不匹配(而且
IsCancellationRequested
false
),因此任务应处于
Faulted
状态:

如果令牌的IsCancellationRequested属性返回false,或者如果异常的令牌与任务的令牌不匹配,则OperationCanceledException将被视为正常异常,导致任务转换为故障状态

当我使用.NET 4.5.2在控制台应用程序中启动此代码时,我得到了处于
已取消
状态的任务(聚合异常包含未知
任务取消执行选项
,而不是原始状态)。原始异常的所有信息丢失(消息、内部异常、自定义数据)

我还注意到,
Task.Wait
的行为与
OperationCanceledException
情况下的
Wait Task
不同

try { Task.Run(() => { throw new InvalidOperationException("123"); }).Wait(); } // 1
catch (AggregateException ae) { Console.WriteLine(ae.InnerException); }

try { await Task.Run(() => { throw new InvalidOperationException("123"); }); } // 2
catch (InvalidOperationException ex) { Console.WriteLine(ex); }

try { Task.Run(() => { throw new OperationCanceledException("123"); }).Wait(); } // 3 
catch (AggregateException ae) { Console.WriteLine(ae.InnerException); }

try { await Task.Run(() => { throw new OperationCanceledException("123"); }); } // 4
catch (OperationCanceledException ex) { Console.WriteLine(ex); }
案例
1
2
产生几乎相同的结果(仅在
StackTrace
中不同),但当我将异常更改为
OperationCanceledException
时,我得到了非常不同的结果:在没有原始数据的案例
3
中出现未知的
TaskCanceledException
,如果
4
包含所有原始数据(消息等),则应出现
OperationCanceledException


所以问题是:MSDN是否包含不正确的信息?或者它是.NET中的一个bug?或者可能只是我不明白什么?

同时,这种行为非常有趣和奇怪。 正如其名称所示,的目的是将应用程序执行期间发生的多个异常/错误组合在一起。因此,在第3种情况下,您有一个
OperationCanceledException
作为内部异常,
AggregateException
的堆栈跟踪应该报告所有相关信息,包括数据(如
123
),如第4种情况下所示:

关于你的问题:

MSDN是否包含不正确的信息

它应该始终报告关于类、方法等行为的正确而精确的信息

或者它是.NET中的一个bug


很可能,是的,这是一个bug。这是不可理解的。您将发现有关此问题的相关问题。请将此问题提交给Microsoft。

这是一个错误<代码>任务。在引擎盖下运行调用
Task.Factory.StartNew
。此内部任务正在获取错误的正确状态。包装任务没有完成

您可以通过调用

Task.Factory.StartNew(() => { throw new OperationCanceledException("123", cts0.Token); }, cts1.Token, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
不过,您将丢失
任务的另一项功能。请运行
,该功能正在展开。请参阅:

更多详细信息:

这是
任务的代码。运行
可以看到它正在创建一个包装
展开
(它源自
任务

外部任务(
unapprovisie
添加一个延续。延续检查内部任务。如果内部任务出现故障,它将查找OperationCanceledException视为指示取消(不考虑匹配的标记)。下面是
unapprovisie.TrySetFromTask
(下面是调用堆栈,显示调用它的位置)。请注意错误状态:

private bool TrySetFromTask(Task task, bool lookForOce)
{
    Contract.Requires(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");

    bool result = false;
    switch (task.Status)
    {
        case TaskStatus.Canceled:
            result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
            break;

        case TaskStatus.Faulted:
            var edis = task.GetExceptionDispatchInfos();
            ExceptionDispatchInfo oceEdi;
            OperationCanceledException oce;
            if (lookForOce && edis.Count > 0 &&
                (oceEdi = edis[0]) != null &&
                (oce = oceEdi.SourceException as OperationCanceledException) != null)
            {
                result = TrySetCanceled(oce.CancellationToken, oceEdi);
            }
            else
            {
                result = TrySetException(edis);
            }
            break;

        case TaskStatus.RanToCompletion:
            var taskTResult = task as Task<TResult>;
            result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
            break;
    }
    return result;
}
private bool TrySetFromTask(任务任务,bool lookForOce)
{
Contract.Requires(task!=null&&task.IsCompleted,“TrySetFromTask:预期任务已完成”);
布尔结果=假;
开关(任务状态)
{
案例任务状态。已取消:
结果=TrySetCanceled(task.CancellationToken,task.GetCancellationExceptionDispatchInfo());
打破
案例任务状态。出现故障:
var edis=task.GetExceptionDispatchInfos();
例外DispatchInfo oceEdi;
操作取消异常oce;
如果(lookForOce&&edis.Count>0&&
(oceEdi=edis[0])!=null&&
(oce=oceEdi.SourceException作为OperationCanceledException)!=null)
{
结果=TrySetCanceled(oce.CancellationToken,oceEdi);
}
其他的
{
结果=TrySetException(edis);
}
打破
案例任务状态.RANTO完成:
var taskTResult=任务作为任务;
结果=TrySetResult(taskTResult!=null?taskTResult.result:default(TResult));
打破
}
返回结果;
}
调用堆栈:

mscorlib.dll!System.Threading.Tasks.Task.TrySetCanceled(System.Threading.CancellationTokenTokenToRecord,object cancellationException)第645行#
mscorlib.dll!System.Threading.Tasks.UnwrapPromise.TrySetFromTask(System.Threading.Tasks.Task任务,bool lookForOce)行6988+0x9f字节C#
mscorlib.dll!System.Threading.Tasks.Unapprovisie.ProcessCompletedOuterTask(System.Threading.Tasks.Task任务)行6956+0xe字节C#
mscorlib.dll!System.Threading.Tasks.UnwrapPromise.InvokeCore(System.Threading.Tasks.Task completingTask)行6910+0x7字节C#
mscorlib.dll!System.Threading.Tasks.UnwrapPromise.Invoke(System.Threading.Tasks.Task completingTask)行6891+0x9字节C#
mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations()行3571 C#
mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree()行2323+0x7字节C#
mscorlib.dll!System.Threading.Tasks.Task.FinishStageTo()行2294+0x7字节C#
mscorlib.dll!System.Threading.Tasks.Task.Finish(boolbuserdelegateexecuted)行2233 C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(参考System.Threading.Tasks.Task currentTaskSlot)行2785+0xc字节#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution)行2728 C#
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThre
internal Task(object state, TaskCreationOptions creationOptions, bool promiseStyle)
{
    Contract.Assert(promiseStyle, "Promise CTOR: promiseStyle was false");

    // Check the creationOptions. We only allow the AttachedToParent option to be specified for promise tasks.
    if ((creationOptions & ~TaskCreationOptions.AttachedToParent) != 0)
    {
        throw new ArgumentOutOfRangeException("creationOptions");
    }

    // m_parent is readonly, and so must be set in the constructor.
    // Only set a parent if AttachedToParent is specified.
    if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
        m_parent = Task.InternalCurrent;

    TaskConstructorCore(null, state, default(CancellationToken), creationOptions, InternalTaskOptions.PromiseTask, null);
}
private bool TrySetFromTask(Task task, bool lookForOce)
{
    Contract.Requires(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");

    bool result = false;
    switch (task.Status)
    {
        case TaskStatus.Canceled:
            result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
            break;

        case TaskStatus.Faulted:
            var edis = task.GetExceptionDispatchInfos();
            ExceptionDispatchInfo oceEdi;
            OperationCanceledException oce;
            if (lookForOce && edis.Count > 0 &&
                (oceEdi = edis[0]) != null &&
                (oce = oceEdi.SourceException as OperationCanceledException) != null)
            {
                result = TrySetCanceled(oce.CancellationToken, oceEdi);
            }
            else
            {
                result = TrySetException(edis);
            }
            break;

        case TaskStatus.RanToCompletion:
            var taskTResult = task as Task<TResult>;
            result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
            break;
    }
    return result;
}
    mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetCanceled(System.Threading.CancellationToken tokenToRecord, object cancellationException) Line 645 C#
    mscorlib.dll!System.Threading.Tasks.UnwrapPromise<System.Threading.Tasks.VoidTaskResult>.TrySetFromTask(System.Threading.Tasks.Task task, bool lookForOce) Line 6988 + 0x9f bytes   C#
    mscorlib.dll!System.Threading.Tasks.UnwrapPromise<System.Threading.Tasks.VoidTaskResult>.ProcessCompletedOuterTask(System.Threading.Tasks.Task task) Line 6956 + 0xe bytes  C#
    mscorlib.dll!System.Threading.Tasks.UnwrapPromise<System.Threading.Tasks.VoidTaskResult>.InvokeCore(System.Threading.Tasks.Task completingTask) Line 6910 + 0x7 bytes   C#
    mscorlib.dll!System.Threading.Tasks.UnwrapPromise<System.Threading.Tasks.VoidTaskResult>.Invoke(System.Threading.Tasks.Task completingTask) Line 6891 + 0x9 bytes   C#
    mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Line 3571    C#
    mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Line 2323 + 0x7 bytes   C#
    mscorlib.dll!System.Threading.Tasks.Task.FinishStageTwo() Line 2294 + 0x7 bytes C#
    mscorlib.dll!System.Threading.Tasks.Task.Finish(bool bUserDelegateExecuted) Line 2233   C#
    mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Line 2785 + 0xc bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Line 2728   C#
    mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Line 2664 + 0x7 bytes   C#
    mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Line 829   C#
    mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Line 1170 + 0x5 bytes   C#
Task.Run(() => { throw new OperationCanceledException("123", cts0.Token); }, cts1.Token);
Task.Run((Action)(() => { throw new OperationCanceledException("123", cts0.Token); }), cts1.Token);