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

Parallel.Foreach - NULL Tasks

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(() =>
      {
        ...
      }));
}