异常处理异步编程
Exception-Handling asynchronous programming
我这里有一些代码。这是真实 class:
的简化版本
public class Delayer
{
//it has to be unawaitable
public async void Execute(Action action)
{
await Task.Delay(10).ConfigureAwait(false);
action.BeginInvoke(null, null); //action.Invoke();
}
}
我用它:
private static Task TestFoo()
{
throw new Exception();
}
delayer.Execute(async () =>
{
//do something else
await TestFoo().ConfigureAwait(false);
});
我无法通过将 Execute
方法传递给 try/catch 来处理此异常,也无法通过将 action.BeginInvoke(null, null)
传递给 try/catch 来处理此异常。我可以处理它,只要我在将异步 lambda 传递给 Execute
方法时用 try/catch 包围它。
我的问题是:async lambda为什么要用await执行?因为如果不使用await执行,exception会被吞掉
我想要 Execute
方法吞下一个动作抛出的所有异常。任何想法如何去做?我做错了什么?
加法:
Execute
的行为必须类似于 "just a fire and forget operation"。
编辑
如果您真的非常想要一个即发即弃的方法,唯一要做的就是
捕获 Execute 方法中的所有异常。但是,如果您希望能够捕获异常而不是在 non-awaitable Action
.
上使用 BeginInvoke
,则必须接受可等待的任务
public class Delayer
{
public async Task Execute(Func<Task> action) // or public async void Execute(Func<Task> action) if you insist on it.
{
try
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
然后你可以安全地做
void CallDelayedMethod()
{
var delayer = new Delayer();
delayer.Execute(ThrowException);
}
public Task ThrowException()
{
throw new Exception();
}
我仍然会 return 一个 Task 并将其留给调用者通过不等待(即发即忘)或不等待来忽略它。
原回答
您在 class Delayer
.
中使用 async void 签名没有遵循最佳实践
public async void Execute(Action action)
应该是
public async Task Execute(Action action)
所以您可以等待 Execute
的电话。否则,它只是一次失火而不管的操作,这使得捕获异常变得困难。通过让它等待你可以做:
try
{
await Delayer.Execute(...);
}
catch(Exception ex)
{
....
}
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.
此外,如果您想将可等待的操作传递给它,您应该让 Execute 接受一个任务:
public async Task Execute(Func<Task> action)
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
我这里有一些代码。这是真实 class:
的简化版本public class Delayer
{
//it has to be unawaitable
public async void Execute(Action action)
{
await Task.Delay(10).ConfigureAwait(false);
action.BeginInvoke(null, null); //action.Invoke();
}
}
我用它:
private static Task TestFoo()
{
throw new Exception();
}
delayer.Execute(async () =>
{
//do something else
await TestFoo().ConfigureAwait(false);
});
我无法通过将 Execute
方法传递给 try/catch 来处理此异常,也无法通过将 action.BeginInvoke(null, null)
传递给 try/catch 来处理此异常。我可以处理它,只要我在将异步 lambda 传递给 Execute
方法时用 try/catch 包围它。
我的问题是:async lambda为什么要用await执行?因为如果不使用await执行,exception会被吞掉
我想要 Execute
方法吞下一个动作抛出的所有异常。任何想法如何去做?我做错了什么?
加法:
Execute
的行为必须类似于 "just a fire and forget operation"。
编辑
如果您真的非常想要一个即发即弃的方法,唯一要做的就是
捕获 Execute 方法中的所有异常。但是,如果您希望能够捕获异常而不是在 non-awaitable Action
.
BeginInvoke
,则必须接受可等待的任务
public class Delayer
{
public async Task Execute(Func<Task> action) // or public async void Execute(Func<Task> action) if you insist on it.
{
try
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
然后你可以安全地做
void CallDelayedMethod()
{
var delayer = new Delayer();
delayer.Execute(ThrowException);
}
public Task ThrowException()
{
throw new Exception();
}
我仍然会 return 一个 Task 并将其留给调用者通过不等待(即发即忘)或不等待来忽略它。
原回答
您在 class Delayer
.
public async void Execute(Action action)
应该是
public async Task Execute(Action action)
所以您可以等待 Execute
的电话。否则,它只是一次失火而不管的操作,这使得捕获异常变得困难。通过让它等待你可以做:
try
{
await Delayer.Execute(...);
}
catch(Exception ex)
{
....
}
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.
此外,如果您想将可等待的操作传递给它,您应该让 Execute 接受一个任务:
public async Task Execute(Func<Task> action)
{
await Task.Delay(10).ConfigureAwait(false);
await action.Invoke();
}