如何创建异步任务方法的任务来捕获异常?

时间:2017-06-26 03:36:04

标签: c# asynchronous async-await task-parallel-library .net-core

我有一个循环读取套接字的任务:

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时,ReceiveAsyncdoWork中发生的异常都是没有附加到readTask。这意味着即使有例外,readTask.StatusRunToCompletereadTask.Exceptionnull。符合。对于文档,这是由于async方法返回void,但是当我尝试这个时:

private async Task readLoop() {...}

它不会编译:

error CS0407: 'Task WebSocketInitiator.readLoop()' has the wrong return type

那么无论如何要让任务在以后开始,同时让它跟踪任务中发生的异常?

显示问题的最小测试用例:https://dotnetfiddle.net/JIfDIn

2 个答案:

答案 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的全部原因是为了避免这些结构。 总之:

  • 如果您需要推迟调用任务
  • ,请转到AsyncLazy
  • 不要调用Task构造函数
  • 使用Task.Run

答案 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 delegateFunc<Task>

Func<Task> func = () => Task.Run(() => readLoop());
...
Task task = func();