记忆障碍和大结构?

时间:2009-11-13 17:30:22

标签: c# multithreading volatile memory-model memory-barriers

假设我有一个由100个字节组成的结构。我对以下代码有什么保证?

m_myLargeStruct = someValue; // copying 100 bytes
Thread.MemoryBarrier();

// Executed by another thread, after "Thread.MemoryBarrier" was called by the first thread
Console.WriteLine(m_myLargeStruct.ToString());

内存模型是否保证在放置内存屏障后完成100字节的复制?或者内存屏障仅适用于处理器架构大小的类型? (32位为4字节,8位为64位) 这就是 volatile 关键字仅适用于基本类型的原因吗? (如果我将一个8字节的成员声明为volatile,这意味着将使用一个互锁的instrinct来改变它的值?[因为32位机器上大于4字节的类型不能保证原子性])。

我希望我很清楚.. :) 感谢

5 个答案:

答案 0 :(得分:10)

除非阅读主题也有内存障碍,否则我不会认为它会对你有所帮助。

就我个人而言,我会回避:

  • 那么大的结构
  • 深入了解内存模型以编写无锁代码

......除非你有一个非常重要的理由这样做。很难用可变数据获得无锁编码; 很难我相信即使是专家也在努力。我经常发现“对每个访问数据的块都采用锁定”的方法更容易实现,99%的情况下性能都很好。

我相信Microsoft的PFX团队能够正确地获得无锁编码,并且他们可以为我提供一些方法,让我可以相对轻松地使用他们的代码来编写我自己的无锁程序。我不相信自己能做到这一点。如果我需要明确使用内存屏障,这可能意味着我在努力。

答案 1 :(得分:2)

在WriteLine之前,您需要在第二个线程中使用另一个内存屏障。 (如果您的系统提供非对称内存屏障,则在分配后执行释放屏障并在WriteLine之前执行获取屏障就足够了。)

数据大小并不重要。

答案 2 :(得分:1)

你需要在两个地方/线程中都有一个内存屏障,当然你需要在两者之间进行某种同步,这样第二个线程的屏障就不会在第一个线程之前“运行”。

具体来说,编写线程需要一个“释放”内存屏障,而读取线程需要一个“获取”内存屏障(如果底层平台支持单独的屏障语义)。

除非您要求学术好奇或者您正在编写自己的框架,否则您应该只使用库/框架/平台中的同步对象。试图让所有这些东西都正确是很棘手的,而且它已经在提供的同步对象中完成了。

答案 3 :(得分:1)

显然,答案是,或者说,您无法保证任何事情。在启动打印出100字节结构的线程之前,没有什么能阻止操作系统交换写入100字节结构的线程。

当您想通过标志或其他原子值协调对数据的访问时,使用内存屏障。我不知道你到底想要做什么,所以我不能给你一些关于你应该怎么做的好的示例代码。

答案 4 :(得分:0)

嗯,首先你不应该有一个那么大的结构。除非你非常小心如何使用结构,否则它将比使用类慢。而且,它与结构的值语义相反。

也就是说,内存屏障将保证结构被复制。优化不会在屏障上移动任何指令。

volatile关键字有点不同。它保证不会对变量的任何操作进行优化,并且它确保了内存访问的顺序。但是,对于无法以原子方式访问的数据类型,它对于线程目的来说几乎没用,因为您仍然可以读取新值的一半和旧值的一半。