我有一个循环读取套接字的任务:
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
。符合。对于文档,这是由于async方法返回void,但是当我尝试这个时:
private async Task readLoop() {...}
它不会编译:
error CS0407: 'Task WebSocketInitiator.readLoop()' has the wrong return type
那么无论如何要让任务在以后开始,同时让它跟踪任务中发生的异常?
显示问题的最小测试用例:https://dotnetfiddle.net/JIfDIn
答案 0 :(得分:2)
如果我理解正确,你想推迟运行任务直到需要它但你想在构造函数中保留创建。这很好,并且有一种模式用于这种情况。它被称为Lazy
但是,懒惰并不是异步。 Stephen Taub has however made an async implementation called AsyncLazy
您收到错误消息的原因是您没有为您创建的外部任务指定返回值。您还需要返回foo
的值。如果你在一个返回内部Task的lambda中包装调用,你可以得到它,如下:
var task1 = new Task<Task>(async () => await foo(),
CancellationToken.None, TaskCreationOptions.LongRunning);
task1.Start();
当你等待它时,你会等待外部任务,而不是它返回的任务。因此你需要解开它:
task1.Unwrap().Wait();
这样你就可以捕获异常。但是,这可能不是最好的方法,因为使用Task.Run,async和await的全部原因是为了避免这些结构。 总之:
答案 1 :(得分:2)
由于任务不需要在创建后立即运行,我使用的是Task构造函数,而不是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();