运行System.Threading.Tasks.Task时出现异常

本文关键字:异常 Task System Threading Tasks 运行 | 更新日期: 2023-09-27 18:07:16

考虑以下使用基本任务库功能和CancellationTokenSource的代码。它启动一个线程,用价格填充Dictionary,并从SQL server数据库读取数据。该线程在大约10分钟后结束,每隔2小时再次启动一次,如果线程仍在运行,则首先调用Cancel。

private CancellationTokenSource mTokenSource = new CancellationTokenSource();
internal Prices(Dictionary<string, Dealer> dealers)
{
    mDealers = dealers;
    mTask = Task.Factory.StartNew
            (() => ReadPrices(mTokenSource.Token), mTokenSource.Token);
}
internal void Cancel() 
{
    mTokenSource.Cancel();
}
private void ReadPrices(CancellationToken ct) 
{
     using (SqlConnection connection = 
            new   SqlConnection(ConfigurationManager.AppSettings["DB"]))   
     {
         connection.Open();
         var dealerIds = from dealer in mDealers.Values 
                         where dealer.Id != null 
                         select dealer.Id;
         foreach (var dealerId in dealerIds) 
         {
             if (!ct.IsCancellationRequested)
             {
                 FillPrices(connection);
             }
             else
                break;
        }
    }
}

现在,在某个时刻,应用程序崩溃,并在事件日志中显示以下异常:

应用程序:Engine.exe框架版本:v4.0.30319描述:进程因未处理的异常而终止。异常信息:系统。AggregateException堆栈:atSystem.Threading.Tasks.TaskExceptionHolder.Finalize ()

它必须与这里的代码,因为任务库不使用其他任何地方,但我不能找出什么是错误的代码。有人知道这里有什么问题吗?

运行System.Threading.Tasks.Task时出现异常

任务喜欢被倾听。听起来好像有什么不开心的事。不过,你确实有"最后一次机会"把话说完:

TaskScheduler.UnobservedTaskException += (sender, args) =>
{
    foreach (var ex in args.Exception.InnerExceptions)
    {
        Log(ex);
    }            
    args.SetObserved();
};

注意,这并不打算作为修复 -它的目的是让您看到Task爆炸和什么错误。SetObserved()将防止它杀死您的应用程序。但是这里的修复最好是:

  • 不要让你的任务抛出,
  • 或者确保你在那里稍后检查任务的状态

很可能对您的取消检测不满意。IIRC的首选方法是:

foreach(...) {
    if(ct.IsCancellationRequested) {
        // any cleanup etc
        ct.ThrowIfCancellationRequested();
    } 
    ...
}

或者更简单,如果不需要清理,只需:

foreach(...) {
    ct.ThrowIfCancellationRequested();
    ...
}

同样,它可能只是一个数据访问异常。在与数据库通信时,可能会发生任意数量的异常。超时、死锁、无法连接等