Parallel.Foreach - NULL任务

时间:2013-09-29 20:09:33

标签: c# task-parallel-library task parallel.foreach

我正在努力学习如何使用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

2 个答案:

答案 0 :(得分:4)

var tasks = new List<Task>();

Parallel.ForEach( 
{   
   tasks.Add(...);
});

List<Task> tasks的使用不是线程安全的。您看到null元素不应该存在,但运行时间稍长,您也可能会看到其他症状和例外情况。行为未定义。

保护对tasks的访问权限,将其替换为ConcurrentBag,或者我的选择完全删除这些任务。您从Parallel.ForEach()得到了足够的并行性。

答案 1 :(得分:2)

您正在看到一个跨线程问题。

此代码块访问非线程安全List<T>,这可能导致不可预测且无确定性错误:

tasks.Add(Task.Factory.StartNew(() =>
    {
         ...
    }));

您需要锁定任务。添加电话:

lock(tasks)
{
    tasks.Add(Task.Factory.StartNew(() =>
      {
        ...
      }));
}