4

我正在尝试学习如何使用 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
4

2 回答 2

6
var tasks = new List<Task>();

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

这种使用List<Task> tasks不是线程安全的。您会看到null不应该出现的元素,但运行时间稍长,您可能还会看到其他症状和异常。行为未定义。

保护对 的访问tasks,将其替换为,ConcurrentBag或者,我的选择,完全放弃这些任务。您从Parallel.ForEach().

于 2013-09-29T20:24:22.413 回答
2

您看到的是跨线程问题。

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

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

您需要锁定任务。添加调用:

lock(tasks)
{
    tasks.Add(Task.Factory.StartNew(() =>
      {
        ...
      }));
}
于 2013-09-29T20:38:00.683 回答