【问题标题】:Parallel.ForEach changes behavior with more than one threadParallel.ForEach 通过多个线程改变行为
【发布时间】:2012-08-13 01:53:34
【问题描述】:

我有一组正在对其执行写操作的文件,我正在使用Parallel.ForEach 循环来加快速度。

问题是,当我有多个线程时,它的行为就会不稳定。该程序向 WAV 文件写入 10 次 250000 个随机字节,这将导致大约 1.3 秒的静态周期。发生的情况是多线程,在绝大多数文件中,没有任何静态,Windows 指示文件已更改,但音频内容没有,另一个问题是某些剪辑有大约 6 分钟的静态写入它,这是不可能的,代码只向每个文件写入 2500000 个字节(约 10.3 秒),因此由于某种原因,它会将字节写入错误的文件,所以有些没有静态,有些则有一个荒谬的数量。它不可能错过静态,所以它不可能是我。我知道出了点问题。

在我多线程程序之前一切正常,所以我使用了MaxDegreeofParallelism = 1,一切正常,所以我知道问题是由多个线程引起的。

Parallel.ForEach(files, new ParallelOptions{MaxDegreeOfParallelism = 4}, file =>
{
Random rand = new Random();
using (stream = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))
{
FileInfo info = new FileInfo(file);
for (int i = 0; i < 10; i++)
{
    int pos = rand.Next(5000000, Convert.ToInt32(info.Length));
    for (int x = 0; x < 250000; x++)
    {
        byte number = array[rand.Next(array.Length)];
        stream.Seek(pos, SeekOrigin.Begin);
        pos += 4;
        stream.WriteByte(number);
    }
}
}
});

编辑:另一个问题是它使用多个线程写入包含关键数据的标题部分,因此没有媒体播放器识别该格式。这只发生在多线程上,我忽略了前 500 万字节,我知道这已经足够了。

编辑:2 更新代码。

谁能解释导致我的代码无法正常运行的多线程是什么?

【问题讨论】:

  • @spender 不是每次迭代都将 pos 增加 4 吗?
  • 您实际上并没有显示 Parallel.ForEach() 位。
  • 为什么每四个写一个字节?那不是一个通道中的 8 位噪音吗?
  • 我已经添加了 Parallel.ForEach 循环的其余部分。我故意一次只写一个频道,这样可以正常工作。
  • 我认为 using() 会导致意外行为。在 .NET 框架中,公共语言运行时 (CLR) 会在不再使用对象时自动释放内存。总是有一个线程运行垃圾收集器来执行此操作。 Parallel.ForEach() 和 using() 语句之间存在崩溃

标签: c# .net multithreading plinq parallel-extensions


【解决方案1】:

stream 声明在由Parallel.ForEach 执行的委托之外Parallel.ForEach 创建的所有线程都在尝试使用相同的变量。由于没有锁定,因此未定义每个线程将具有的 stream 的值 - 您可能会看到多个线程写入同一个流,这会导致您出现奇怪的多线程行为。

尝试在委托之外删除stream 的声明,然后进行更改

using (stream = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))

using (Stream stream = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))

【讨论】:

  • 我总是盯着问题看,但我似乎看不到它。它现在正在正确写入所有文件,并且标题保持不变,谢谢。
【解决方案2】:

Random number generator 不是线程安全的。 Jon Skeet 对此有一个 good article 以及如何制作线程安全的随机数生成器。

【讨论】:

  • 我已经更新了代码,我没有复制所有内容。 Rand 被声明为线程静态,并为每个文件生成一个新实例;我需要做更多的事情来保证它的线程安全吗?
  • 我认为这行不通。在.ForEach() 中用Random rand = new Random(); 声明你的随机数生成器,而不是让它成为一个类级别的变量。如果ThreadStaticAttribute 在非静态变量上,我认为它不会做任何事情(请参阅:stackoverflow.com/questions/5227676/…
  • 我尝试在Parallel.ForEach循环中实例化rand,但是没有效果,问题依然存在。但是当我再次运行测试时,它带来了另一个我忘记的问题,写入文件的标题部分,使其无法读取。我的问题中的代码已更新。
  • @JesseC.Slicer 我认为这也行不通。很可能多个线程将使用相同的种子初始化它们的Random 副本。改用 Skeet 的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
相关资源
最近更新 更多