C# 返回不同状态代码的任务中的类似代码

C# 返回不同状态代码的任务中的类似代码,c#,.net,asynchronous,task,C#,.net,Asynchronous,Task,我从三个不同的任务中抛出一个操作取消异常,每个任务都有细微的差异,如下代码所示: static async Task ThrowCancellationException() { throw new OperationCanceledException(); } static void Main(string[] args) { var t1 = new Task(() => throw new OperationCanceledException()); t1.

我从三个不同的任务中抛出一个
操作取消异常
,每个任务都有细微的差异,如下代码所示:

static async Task ThrowCancellationException()
{
    throw new OperationCanceledException();
}

static void Main(string[] args)
{
    var t1 = new Task(() => throw new OperationCanceledException());
    t1.Start();
    try { t1.Wait(); } catch { }

    Task t2 = new Task(async () => throw new OperationCanceledException());
    t2.Start();
    try { t2.Wait(); } catch { }

    Task t3 = ThrowCancellationException();

    Console.WriteLine(t1.Status); // prints Faulted
    Console.WriteLine(t2.Status); // prints RanToCompletion
    Console.WriteLine(t3.Status); // prints Canceled
}
我的问题是:

为什么每个任务的状态不同?

我可以理解,标有
async
的代码/lambda与未标有
async
的lambda之间存在差异,但即使
async
lambda与运行相同代码的
async
方法之间的状态也不同

我可以理解,标记为async的代码/lambda和未标记为async的lambda之间存在差异,但即使在运行相同代码的async lambda和async方法之间,状态也不同

这不完全是真的

如果仔细查看该
新任务(async()=>抛出新操作CanceledException())
,您将看到它正在调用重载
新任务(操作操作)
(没有重载需要
函数)。这意味着它相当于传递一个
async void
方法,而不是
async Task
方法


因此:

这可编译为以下内容:

private static async void CompilerGeneratedMethod()
{
    throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }
private static async Task CompilerGeneratedMethod()
{
    throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);
这将从线程池中获取一个线程,并在其上运行
CompilerGeneratedMethod
。当从
async void
方法内部抛出异常时,异常会在适当的位置重新抛出(在本例中,它会在线程池中重新抛出),但
CompilerGeneratedMethod
方法本身会立即返回。这会导致
任务t2
立即完成,这就是其状态为
RanToCompletion
的原因

那么,例外情况发生了什么?这将使你的申请失败!将
控制台。ReadLine
粘贴在
主控制台的末尾,查看应用程序是否在您有机会按enter键之前退出


这:

这是非常不同的。它没有试图在线程池上运行任何东西
ThrowCancellationException
同步运行,并同步返回一个
任务
,该任务包含
操作取消异常
。包含
操作取消异常的
任务
被视为
已取消


如果要在线程池上运行
async
方法,请使用
Task.run
。这有一个重载,它接受一个
Func
,这意味着:

Task t2 = Task.Run(async () => throw new OperationCanceledException());
编译为如下内容:

private static async void CompilerGeneratedMethod()
{
    throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }
private static async Task CompilerGeneratedMethod()
{
    throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);
这里,当对线程池执行
CompilerGeneratedMethod
时,它返回一个
任务
,其中包含
操作取消异常
。然后,任务机器将
任务t2
转换为
已取消
状态



另一方面,避免使用
新任务
,如果要在线程池上显式运行方法,则更喜欢使用
任务。运行
。TPL中有许多方法是在async/await之前引入的,与之一起使用时容易混淆。

当您使用
任务
构造函数创建任务时,可以提供
取消令牌
作为可选参数。只有当与此特定的
取消令牌
关联的
操作取消异常
时,该任务才会导致
取消
状态。否则,异常将被解释为故障。以下是如何将
操作取消异常
取消令牌
相关联:

var cts = new CancellationTokenSource();
var t1 = new Task(() =>
{
    cts.Cancel();
    throw new OperationCanceledException(cts.Token);
}, cts.Token);
关于使用带有异步委托的
任务
构造函数作为参数,正确的方法是创建嵌套的
任务

Task t2=新任务(异步()=>抛出新操作canceledException());
t2.Start();
尝试{t2.Result.Wait();}捕获{}
Console.WriteLine(t2.Result.Status);//取消打印

“但即使在异步lambda和运行相同代码的异步方法之间,状态也不同”——不正确。
newtask
的重载将采取
操作,而不是
Func
,这意味着
async
lambda版本相当于传递一个
async void
方法,而不是
async Task
方法。使用
Task.Run
如果您想传入一个返回
任务的委托:它有一个重载,它接受
Func
好的,因此在异步lambda和异步方法之间,返回类型会有所不同。我不知道。另外,在Main的末尾粘贴一个Console.ReadLine,并确保应用程序在您有机会按enter键之前退出。-事实上,我在最后用断点检查它,没有注意到异常。回答得好@这是相关的位,但是
t2
t3
之间还有另一个区别:
t2
使用
新任务(…).Start()
,它在线程池上运行给定的方法
t3
直接调用
ThrowCancellationException
,线程池就不参与了。我明白了,谢谢你这么说,我不知道线程池什么时候参与,什么时候不参与。因此,如果我调用了任务的构造函数并将该方法作为参数提供,那么它将在我调用start时进入线程池,但直接调用将在当前线程上运行它,直到它到达我正确理解的
wait
id。@meJustAndrew这是正确的。用
Task.Run
替换
newtask
更清楚一点:它在线程池上运行给定的方法<代码>新任务
async/await
出现之前就被引入了,现在还不清楚
任务
最终是否会代表“一点可能尚未完成的异步工作”,而不是“在另一个可能尚未完成的线程上同步运行的代码”。如果
任务
Task<Task> t2 = new Task<Task>(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Result.Wait(); } catch { }

Console.WriteLine(t2.Result.Status); // prints Canceled