【问题标题】:Concurrently write to an object in C#在 C# 中同时写入对象
【发布时间】:2012-01-19 15:34:17
【问题描述】:

我正在用 C# 创建一个图像处理应用程序,并尝试通过并行执行大部分处理来利用我的多核系统。具体来说,我有一个 Parallel.For 循环遍历每个像素并应用高斯滤波器。正如预期的那样,仅执行计算,我就看到了显着的加速,但是在尝试保存计算结果时出现了问题。代码如下所示:

Parallel.For(// various loop parameters
{
    // processing and number crunching
    // significant speed up here
    .
    .
    .
    bitmap.SetPixel(x, y, value); 
});

这会产生运行时错误,因为多个线程最终会尝试同时写入位图对象。我将对象锁定如下:

lock(bitmap)
    bitmap.SetPixel(x, y, value); 

但这最终会比串行版本慢。检查后发现,每次循环迭代对 SetPixel 的调用占用了大约 90% 的运行时间,这意味着单独的线程将大部分时间用于等待获取位图对象上的锁,并且增加了开销拥有多个线程会减慢它的速度。

所以我想知道的是,有没有办法让多个线程同时写入同一个对象?我很小心每个线程都在处理图像的不同部分,因此不会引入竞争条件。有没有办法覆盖抛出的错误并说:“我知道我正在做的事情是不安全的,但我还是想这样做?” Concurrency::combinable 类之类的东西似乎可以解决问题,但这仅在 C++ 中。

【问题讨论】:

  • 将位图分割成几个不同的对象,并行处理它们,完成后重新创建完整的东西是否可行?
  • 好问题。实际上我已经尝试过了——我手动创建了自己的线程,给它们每个单独的位图并在最后手动组合它们,但是增加的开销也最终使它比串行版本慢。

标签: c# concurrency parallel-processing


【解决方案1】:

如果您在整个图像中进行过滤,您应该考虑使用locking 位图,将图像数据复制到数组中,然后同时更新数组数据,而不是调用SetPixel

【讨论】:

  • 从正确性的角度来看,写入数组中的不同位置应该是非常安全的,但是您需要确保正确地进行写入以避免false sharing
  • @RonWarholic:我相信用户无论如何都需要两个位图实例(只读源和可写目标)。不应该有任何虚假共享,因为目标没有被其他线程读取(如果我没记错的话)?
  • @Groo:只要您从未从数组中读取任何内容(只写),您就不会遇到错误共享,但如果有任何线程读取,您就会遇到问题。由于 OP 正在应用高斯过滤器,因此他不需要读取,因此错误共享可能不会成为问题,但您需要牢记这一点,以免意外引入大的性能损失。
  • 谢谢李,成功了。郑重声明,Ron 是对的,我只需要一个位图对象。
猜你喜欢
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多