考虑以下代码,该代码使用带有CancellationTokenSource的基本任务库功能。它启动一个线程,用字符填充字典并从SQL服务器db读取数据。线程在大约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描述: 由于未处理的异常,进程被终止。例外信息: System.AggregateException Stack:at System.Threading.Tasks.TaskExceptionHolder.Finalize()
它必须与这里的代码有关,因为在其他任何地方都没有使用Tasks库,但我无法弄清楚代码有什么问题。有谁知道这里有什么问题?
答案 0 :(得分:19)
喜欢听的任务。听起来有点不开心。不过,你确实得到了“最后一次机会”:
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();
...
}
同样,它可能只是一个数据访问异常。与数据库通信时可能会发生任意数量的异常。超时,死锁,无法连接等。