【问题标题】:Task.Factory.StartNew() doesn't include Exception when using ContinueWith()Task.Factory.StartNew() 使用 ContinueWith() 时不包含异常
【发布时间】:2024-04-12 02:30:01
【问题描述】:

我正在尝试创建一个不会阻塞我的主线程但仍会记录错误以防出现异常的并行进程。最初我有这个代码:

var task = Task.Run(
    () => _db.addData());

task.ContinueWith(continuationTask =>
    {
        if (continuationTask.Exception != null)
        {
            _logger.LogError(continuationTask.Exception, "Failed to log mismatch");
        }
    },
    _taskScheduler);

运行应用程序时,代码_taskScheduler 设置为TaskScheduler.Current。 运行时,它会正确并行执行代码,当数据库出现问题时,continuationTask.Exception 包含相应的异常消息。

指定TaskScheduler 的目的是让我可以控制单元测试中的执行顺序。就目前而言,在单元测试中,原始任务和延续将使用不同的任务调度程序运行 - 分别为 TaskSchedulerMockTaskScheduler。 (为简洁起见,没有显示单元测试代码,但MockTaskScheduler 只是一个自定义实现,它允许我排队和执行任务)

由于Task.Run 不采用TaskScheduler 作为参数,我尝试将代码更改为

var task = Task.Factory.StartNew(
    () => _db.addData(),
    CancellationToken.None,
    TaskCreationOptions.None,
    _taskScheduler);

task.ContinueWith(continuationTask =>
    {
        if (continuationTask .Exception != null)
        {
            _logger.LogError(continuationTask.Exception, "Failed to log mismatch");
        }
    },
    _taskScheduler);

这使我的测试工作完美,因为我现在可以控制每个任务何时执行,因为它们使用相同的可注入 _taskScheduler。但是,当使用TaskScheduler.Current 运行应用程序代码时,continuationTask.Exception 始终为 null,从而阻止我记录任何异常。

Task.Run 填充 continuationTask.ExceptionTask.Factory.StartNew 不填充有什么原因吗?我错过了什么吗?

【问题讨论】:

  • 当前调度器是因为ContinueWith而不是Task.Run设置的。使用await Task.Run.
  • 我不想等待任务,这会使我的代码同步。
  • 您的代码已经是异步的,只是使用了另一种语法。
  • 根据addData的返回类型,很可能第二种情况下的taskTask<Task>。 Task.Run 会像这样自动解开内部任务,但如果您使用 StartNew,则必须手动执行。试试task.Unwrap().ContinueWith(...
  • 感谢@MikeZboray 成功了。 addData 的返回类型确实是一项任务。正如建议的那样,似乎内部任务的例外正在丢失。添加task.Unwrap().ContinueWith() 解决了这个问题。如果您将此添加为答案,我会将其设置为已批准的答案。

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


【解决方案1】:

正如 @Mike Zboray 在 cmets 中提到的,我需要调用 task.Unwrap().ContinueWith(),因为我有嵌套任务。这仅在使用Task.Factory.StartNew() 时才有必要,因为Task.Run() 会自动解开嵌套任务。

我的最终代码如下所示:

var task = Task.Factory.StartNew(
    () => _db.addData(),
    CancellationToken.None,
    TaskCreationOptions.None,
    _taskScheduler);

task.Unwrap().ContinueWith(continuationTask =>
    {
        if (continuationTask .Exception != null)
        {
            _logger.LogError(continuationTask.Exception, "Failed to log mismatch");
        }
    },
    _taskScheduler);

【讨论】:

    最近更新 更多