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