如何创建异步任务方法的任务来捕获异常?
How to create a Task of an async Task method to catch the exceptions?
我有一个任务正在套接字上循环读取:
private async void readLoop() {
while (!cts.IsCancelRequested()) {
await socket.ReceiveAsync(data, ...);
doWork(data);
}
}
并且由于任务不需要 运行 创建后,我使用 Task
构造函数,而不是 Task.Run
。
private Task readTask = new Task(readLoop, cts, LongRunning);
// when the task need to run:
readTask.Start()
此时它工作正常,除了当任务需要完成时,当我调用readTask.Wait()
或await readTask
时,异常发生在ReceiveAsync
或doWork
没有附加到 readTask
。这意味着即使有例外,readTask.Status
也是 RunToComplete
而 readTask.Exception
是 null
。符合。对文档来说,这是由于异步方法 returns 无效,但是当我尝试这个时:
private async Task readLoop() {...}
它不会编译:
error CS0407: 'Task WebSocketInitiator.readLoop()' has the wrong return type
那么有没有办法让任务在稍后的时间开始,同时让它跟踪任务中发生的异常?
显示问题的最小测试用例:https://dotnetfiddle.net/JIfDIn
如果我对你的理解正确,你想推迟 运行 任务直到需要它,但你想将创建保留在构造函数中。这很好,并且有一种模式用于此类情况。它被称为 Lazy
.
但是,Lazy 不是异步的。 Stephen Taub has however made an async implementation called AsyncLazy.
您收到错误消息的原因是您没有为创建的外部任务指定 return 值。您还需要 return foo
的值。如果你将调用包装在一个 lambda 中,它是 return 的内部任务,你可以得到它,如下所示:
var task1 = new Task<Task>(async () => await foo(),
CancellationToken.None, TaskCreationOptions.LongRunning);
task1.Start();
然而,当您等待它时,您将等待外部任务而不是它 return 的任务。因此你需要打开它:
task1.Unwrap().Wait();
这样你就可以捕捉到异常。然而,这可能不是最好的方法,因为使用 Task.Run、async 和 await 的全部原因是为了避免这些构造。
结论:
- 如果您需要推迟任务的调用,请选择 AsyncLazy
- 不要调用任务构造函数
- 使用Task.Run
And since the task need not run as soon as it is created, I'm using the Task constructor, rather than Task.Run.
永远不要使用任务构造函数。 It has literally no rational use cases at all.
在你的情况下,听起来你不需要委托或惰性初始化,或类似的东西;由于您的成员和方法是私有的,因此在执行时分配任务成员同样有意义:
private async Task readLoop();
// when the task needs to run:
readTask = Task.Run(() => readLoop());
但是,如果您 需要在将来而不是现在向 运行 表示一些代码,那么您需要一个委托。在这种情况下,asynchronous delegate:Func<Task>
:
Func<Task> func = () => Task.Run(() => readLoop());
...
Task task = func();
我有一个任务正在套接字上循环读取:
private async void readLoop() {
while (!cts.IsCancelRequested()) {
await socket.ReceiveAsync(data, ...);
doWork(data);
}
}
并且由于任务不需要 运行 创建后,我使用 Task
构造函数,而不是 Task.Run
。
private Task readTask = new Task(readLoop, cts, LongRunning);
// when the task need to run:
readTask.Start()
此时它工作正常,除了当任务需要完成时,当我调用readTask.Wait()
或await readTask
时,异常发生在ReceiveAsync
或doWork
没有附加到 readTask
。这意味着即使有例外,readTask.Status
也是 RunToComplete
而 readTask.Exception
是 null
。符合。对文档来说,这是由于异步方法 returns 无效,但是当我尝试这个时:
private async Task readLoop() {...}
它不会编译:
error CS0407: 'Task WebSocketInitiator.readLoop()' has the wrong return type
那么有没有办法让任务在稍后的时间开始,同时让它跟踪任务中发生的异常?
显示问题的最小测试用例:https://dotnetfiddle.net/JIfDIn
如果我对你的理解正确,你想推迟 运行 任务直到需要它,但你想将创建保留在构造函数中。这很好,并且有一种模式用于此类情况。它被称为 Lazy
.
但是,Lazy 不是异步的。 Stephen Taub has however made an async implementation called AsyncLazy.
您收到错误消息的原因是您没有为创建的外部任务指定 return 值。您还需要 return foo
的值。如果你将调用包装在一个 lambda 中,它是 return 的内部任务,你可以得到它,如下所示:
var task1 = new Task<Task>(async () => await foo(),
CancellationToken.None, TaskCreationOptions.LongRunning);
task1.Start();
然而,当您等待它时,您将等待外部任务而不是它 return 的任务。因此你需要打开它:
task1.Unwrap().Wait();
这样你就可以捕捉到异常。然而,这可能不是最好的方法,因为使用 Task.Run、async 和 await 的全部原因是为了避免这些构造。 结论:
- 如果您需要推迟任务的调用,请选择 AsyncLazy
- 不要调用任务构造函数
- 使用Task.Run
And since the task need not run as soon as it is created, I'm using the Task constructor, rather than Task.Run.
永远不要使用任务构造函数。 It has literally no rational use cases at all.
在你的情况下,听起来你不需要委托或惰性初始化,或类似的东西;由于您的成员和方法是私有的,因此在执行时分配任务成员同样有意义:
private async Task readLoop();
// when the task needs to run:
readTask = Task.Run(() => readLoop());
但是,如果您 需要在将来而不是现在向 运行 表示一些代码,那么您需要一个委托。在这种情况下,asynchronous delegate:Func<Task>
:
Func<Task> func = () => Task.Run(() => readLoop());
...
Task task = func();