我有一个项目以非常相似的方式执行多个操作(订阅完成事件、执行任务、取消订阅完成事件以及处理取消、超时等)所以我决定编写一个实用程序类来处理这些操作执行。但是,我遇到了一个我不理解的场景,因此不知道如何解决。
这个过于简化的代码说明了问题:
class Program
{
static void Main(string[] args)
{
Do();
Console.Read();
}
private static async Task Do()
{
var task = new Task(async() => await Operation()/*this throws and terminates the application*/);
try
{
await OperationExecuter.ExecuteAsync(task);
}
catch (InvalidOperationException)
{
//I expected the exception to be caught here
}
}
static async Task Operation()
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
}
class OperationExecuter
{
public static async Task ExecuteAsync(Task task)
{
task.Start();
await task; //I expected the exception to be unwrapped and thrown here
}
}
我也尝试过像 var task = new Task(() => Operation());
这样的任务,但从未处理异常(尽管它不会终止应用程序,因为它不会在主线程中引发)。
如何正确处理异常?
更改实现以采取行动会产生相同的结果:
class Program
{
static void Main(string[] args)
{
Do();
Console.Read();
}
private static async Task Do()
{
var action = new Action(async () => await Operation() /*this throws and terminates the application*/);
try
{
await OperationExecuter.ExecuteAsync(action);
}
catch (InvalidOperationException)
{
//I expected the exception to be caught here
}
}
static async Task Operation()
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
}
class OperationExecuter
{
public static async Task ExecuteAsync(Action action)
{
await Task.Run(action); //I expected the exception to be unwrapped and thrown here
}
}
对于好奇的人来说,更现实的 OperationExecuter
应该是这样的:
class Program
{
static void Main(string[] args)
{
Do();
Do2();
Console.Read();
}
private static async Task Do()
{
var service = new Service(new Hardware());
try
{
await
OperationExecuter.ExecuteAsync(service, handler => service.Operation1Completed += handler,
handler => service.Operation1Completed += handler, async () => await service.Operation1(),
CancellationToken.None);
}
catch (InvalidOperationException)
{
//Exception is caught!!!
}
}
private static async Task Do2()
{
var service = new Service(new Hardware());
try
{
await
OperationExecuter.ExecuteAsync(service, handler => service.Operation1Completed += handler,
handler => service.Operation1Completed += handler, async () => await service.Operation2(60),
CancellationToken.None);
}
catch (InvalidOperationException)
{
//Exception is caught!!!
}
}
}
internal class OperationExecuter
{
public static async Task ExecuteAsync(Service service, Action<EventHandler> subscriptionAction,
Action<EventHandler> unsubscriptionAction, Func<Task> sendCommandAction, CancellationToken cancellationToken)
{
var commandCompletionSource = new TaskCompletionSource<bool>();
var hardwareFailureCompletionSource = new TaskCompletionSource<bool>();
cancellationToken.Register(() => commandCompletionSource.SetCanceled());
var eventHandler = new EventHandler((sender, args) =>
{
commandCompletionSource.SetResult(true);
});
service.HardwareFailure += (sender, args) => hardwareFailureCompletionSource.SetResult(false);
subscriptionAction(eventHandler);
try
{
await Task.Run(sendCommandAction, cancellationToken);
await Task.WhenAny(commandCompletionSource.Task, hardwareFailureCompletionSource.Task);
//same for disconnection, etc
if (hardwareFailureCompletionSource.Task.IsCompleted)
{
throw new HardwareFailureException();
}
}
finally
{
unsubscriptionAction(eventHandler);
}
}
}
class HardwareFailureException : Exception
{
}
class Service
{
private readonly Hardware hardware;
public Service(Hardware hardware)
{
this.hardware = hardware;
}
public async Task Operation1() //something like sending command to hardware
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
public event EventHandler Operation1Completed;
public async Task Operation2(int someParameter)
{
await Task.Delay(1000);
throw new InvalidOperationException();
}
public event EventHandler Operation2Completed;
public event EventHandler LostConnection;
public event EventHandler HardwareFailure;
}
class Hardware
{
}
最佳答案
问题是由于您实际上创建了一个 Task<Task>
。 ,而你只有await
外部Task
.这是您不应该使用 Task
的原因之一。构造函数。相反,使用 Task.Run
,它知道这一点并将为您解包外部任务:
private static async Task Do()
{
var task = Task.Run(async() => await Operation());
try
{
await OperationExecuter.ExecuteAsync(task);
}
catch (InvalidOperationException)
{
//I expected the exception to be caught here
}
}
编辑:
@Servy 正确地指出,除非有特别好的理由,否则您要包装 Task
与 Task.Run
,您可以将它们全部保存在一起并简单地 await
在创建Task
一起省去解包的麻烦:
public class OperationExecuter
{
public static async Task ExecuteAsync(Func<Task> func)
{
await func();
}
}
关于c# - 如何使用显式构造的任务处理异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32506652/