Parallel.Foreach - NULL Tasks
本文关键字:Tasks NULL Foreach Parallel | 更新日期: 2023-09-27 18:33:18
我正在尝试学习如何使用TPL。我已经对这个主题做了相当多的阅读,但我不明白为什么下面的代码示例会中断,而后面的代码示例会起作用?
我正在运行一个单元测试来计算写入的记录,并读取输出的文件以进行仔细检查。
失败:
var tasks = new List<Task>();
Parallel.ForEach(File.ReadLines(featuresLocation), s =>
{
var feature = CreateFeature(s);
if (feature.HasValue)
{
tasks.Add(Task.Factory.StartNew(() =>
{
lock (_locker)
{
featuresWriter.WriteLine(feature.Value);
RecordsWrote++;
}
}));
}
});
Task.WaitAll(tasks.ToArray()); // Breaks
加工:
var tasks = new List<Task>();
Parallel.ForEach(File.ReadLines(featuresLocation), s =>
{
var feature = CreateFeature(s);
if (feature.HasValue)
{
tasks.Add(Task.Factory.StartNew(() =>
{
lock (_locker)
{
featuresWriter.WriteLine(feature.Value);
RecordsWrote++;
}
}));
}
});
Task.WaitAll(tasks.Where(x => x != null).ToArray()); // Works
var tasks = new List<Task>();
Parallel.ForEach(
{
tasks.Add(...);
});
这种List<Task> tasks
的使用不是线程安全的。您看到null
不应该存在的元素,但运行时间更长,您可能还会看到其他症状和异常。行为未定义。
保护对tasks
的访问,将其替换为ConcurrentBag
,或者,我的选择,完全删除这些任务。您从Parallel.ForEach()
获得了足够的并行性。
您看到的是跨线程问题。
此代码块访问非线程安全List<T>
,这可能导致不可预测且无确定性错误:
tasks.Add(Task.Factory.StartNew(() =>
{
...
}));
您需要锁定任务。添加呼叫:
lock(tasks)
{
tasks.Add(Task.Factory.StartNew(() =>
{
...
}));
}