Tim*_*hyP 256 c# asynchronous exception-handling task-parallel-library async-await
使用Microsoft for .NET的异步CTP,是否可以捕获调用方法中异步方法抛出的异常?
public async void Foo()
{
var x = await DoSomethingAsync();
/* Handle the result, but sometimes an exception might be thrown.
For example, DoSomethingAsync gets data from the network
and the data is invalid... a ProtocolException might be thrown. */
}
public void DoFoo()
{
try
{
Foo();
}
catch (ProtocolException ex)
{
/* The exception will never be caught.
Instead when in debug mode, VS2010 will warn and continue.
The deployed the app will simply crash. */
}
}
Run Code Online (Sandbox Code Playgroud)
所以基本上我希望异步代码中的异常冒泡到我的调用代码中,如果这甚至可能的话.
Stu*_*art 241
阅读有点奇怪,但是,异常会冒泡到调用代码 - 但只有你await
或者Wait()
调用Foo
.
public async Task Foo()
{
var x = await DoSomethingAsync();
}
public async void DoFoo()
{
try
{
await Foo();
}
catch (ProtocolException ex)
{
// The exception will be caught because you've awaited
// the call in an async method.
}
}
//or//
public void DoFoo()
{
try
{
Foo().Wait();
}
catch (ProtocolException ex)
{
/* The exception will be caught because you've
waited for the completion of the call. */
}
}
Run Code Online (Sandbox Code Playgroud)
异步void方法具有不同的错误处理语义.当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上.使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发.- https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
请注意,如果.Net决定同步执行您的方法,则使用Wait()可能会导致应用程序阻塞.
这种解释http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions是相当不错的-它讨论了编译器会实现这一神奇的步骤.
Rob*_*rch 71
未捕获异常的原因是因为Foo()方法具有void返回类型,因此当调用await时,它只返回.由于DoFoo()不等待Foo的完成,因此不能使用异常处理程序.
如果您可以更改方法签名Foo()
,则会打开一个更简单的解决方案 - 更改以便返回类型Task
,然后DoFoo()
可以await Foo()
像在此代码中一样:
public async Task Foo() {
var x = await DoSomethingThatThrows();
}
public async void DoFoo() {
try {
await Foo();
} catch (ProtocolException ex) {
// This will catch exceptions from DoSomethingThatThrows
}
}
Run Code Online (Sandbox Code Playgroud)
Alo*_*aus 18
您的代码没有按照您的想象执行.方法开始等待异步结果后,异步方法立即返回.使用跟踪来研究代码的实际行为是很有见地的.
以下代码执行以下操作:
static TypeHashes _type = new TypeHashes(typeof(Program));
private void Run()
{
TracerConfig.Reset("debugoutput");
using (Tracer t = new Tracer(_type, "Run"))
{
for (int i = 0; i < 4; i++)
{
DoSomeThingAsync(i);
}
}
Application.Run(); // Start window message pump to prevent termination
}
private async void DoSomeThingAsync(int i)
{
using (Tracer t = new Tracer(_type, "DoSomeThingAsync"))
{
t.Info("Hi in DoSomething {0}",i);
try
{
int result = await Calculate(i);
t.Info("Got async result: {0}", result);
}
catch (ArgumentException ex)
{
t.Error("Got argument exception: {0}", ex);
}
}
}
Task<int> Calculate(int i)
{
var t = new Task<int>(() =>
{
using (Tracer t2 = new Tracer(_type, "Calculate"))
{
if( i % 2 == 0 )
throw new ArgumentException(String.Format("Even argument {0}", i));
return i++;
}
});
t.Start();
return t;
}
Run Code Online (Sandbox Code Playgroud)
当你观察痕迹
22:25:12.649 02172/02820 { AsyncTest.Program.Run
22:25:12.656 02172/02820 { AsyncTest.Program.DoSomeThingAsync
22:25:12.657 02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 0
22:25:12.658 02172/05220 { AsyncTest.Program.Calculate
22:25:12.659 02172/02820 { AsyncTest.Program.DoSomeThingAsync
22:25:12.659 02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 1
22:25:12.660 02172/02756 { AsyncTest.Program.Calculate
22:25:12.662 02172/02820 { AsyncTest.Program.DoSomeThingAsync
22:25:12.662 02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 2
22:25:12.662 02172/02820 { AsyncTest.Program.DoSomeThingAsync
22:25:12.662 02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 3
22:25:12.664 02172/02756 } AsyncTest.Program.Calculate Duration 4ms
22:25:12.666 02172/02820 } AsyncTest.Program.Run Duration 17ms ---- Run has completed. The async methods are now scheduled on different threads.
22:25:12.667 02172/02756 Information AsyncTest.Program.DoSomeThingAsync Got async result: 1
22:25:12.667 02172/02756 } AsyncTest.Program.DoSomeThingAsync Duration 8ms
22:25:12.667 02172/02756 { AsyncTest.Program.Calculate
22:25:12.665 02172/05220 Exception AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 0
at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124
at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
22:25:12.668 02172/02756 Exception AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 2
at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124
at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
22:25:12.724 02172/05220 } AsyncTest.Program.Calculate Duration 66ms
22:25:12.724 02172/02756 } AsyncTest.Program.Calculate Duration 57ms
22:25:12.725 02172/05220 Error AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 0
Server stack trace:
at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124
at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Exception rethrown at [0]:
at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()
at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()
at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 106
22:25:12.725 02172/02756 Error AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 2
Server stack trace:
at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124
at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Exception rethrown at [0]:
at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()
at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()
at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 0
22:25:12.726 02172/05220 } AsyncTest.Program.DoSomeThingAsync Duration 70ms
22:25:12.726 02172/02756 } AsyncTest.Program.DoSomeThingAsync Duration 64ms
22:25:12.726 02172/05220 { AsyncTest.Program.Calculate
22:25:12.726 02172/05220 } AsyncTest.Program.Calculate Duration 0ms
22:25:12.726 02172/05220 Information AsyncTest.Program.DoSomeThingAsync Got async result: 3
22:25:12.726 02172/05220 } AsyncTest.Program.DoSomeThingAsync Duration 64ms
Run Code Online (Sandbox Code Playgroud)
您将注意到Run方法在线程2820上完成,而只有一个子线程已完成(2756).如果你在await方法周围放置一个try/catch,你可以用通常的方式"捕获"异常,尽管你的代码在计算任务完成并执行你的contiuation时在另一个线程上执行.
计算方法会自动跟踪抛出的异常,因为我确实使用了ApiChange工具中的ApiChange.Api.dll.跟踪和反射器有助于理解正在发生的事情.要摆脱线程,您可以创建自己的GetAwaiter BeginAwait和EndAwait版本,并且不包含任务,例如在您自己的扩展方法中使用Lazy和trace.然后,您将更好地了解编译器和TPL的作用.
现在你看到没有办法尝试/捕获你的异常,因为没有剩余的堆栈帧来传播任何异常.在启动异步操作后,您的代码可能会执行完全不同的操作.它可能会调用Thread.Sleep甚至终止.只要剩下一个前台线程,您的应用程序就会很乐意继续执行异步任务.
在异步操作完成并回调到UI线程后,您可以在异步方法内处理异常.建议的方法是使用TaskScheduler.FromSynchronizationContext.只有当你有一个UI线程而且它并不忙于其他事情时,这才有效.
可以在异步函数中捕获异常。
public async void Foo()
{
try
{
var x = await DoSomethingAsync();
/* Handle the result, but sometimes an exception might be thrown
For example, DoSomethingAsync get's data from the network
and the data is invalid... a ProtocolException might be thrown */
}
catch (ProtocolException ex)
{
/* The exception will be caught here */
}
}
public void DoFoo()
{
Foo();
}
Run Code Online (Sandbox Code Playgroud)
小智 5
同样重要的是要注意,如果异步方法有 void 返回类型,您将丢失异常的按时间顺序排列的堆栈跟踪。我建议按如下方式返回 Task。将使调试变得更加容易。
public async Task DoFoo()
{
try
{
return await Foo();
}
catch (ProtocolException ex)
{
/* Exception with chronological stack trace */
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
190828 次 |
最近记录: |