2

假设我有一个由 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 个字节,64 位为 8 个字节)。
这就是volatile关键字仅适用于原始类型的原因吗?(如果我将一个 8 字节的成员声明为 volatile,这意味着将使用互锁的 instrinct 来更改它的值?[因为在 32 位机器上不能保证大于 4 字节的类型的原子性])。

我希望我足够清楚.. :)
谢谢

4

5 回答 5

10

除非阅读线程也有内存障碍,否则我认为它不会对您有太大帮助。

我个人会回避:

  • 这么大的结构
  • 深入内存模型编写无锁代码

...除非您有非常重要的理由这样做。对可变数据进行正确的无锁编码是非常困难的我相信即使是专家也很挣扎。我通常发现“为访问数据的每个块锁定”方法更容易正确,并且在 99% 的情况下性能良好。

我相信 Microsoft 的 PFX 团队能够正确地进行无锁编码,并且他们会为我提供可以使用他们的代码相对轻松地编写自己的无锁程序的方法。我不相信自己能把这种事情做好。如果我需要明确使用内存屏障,那可能意味着我太努力了。

于 2009-11-13T17:37:24.817 回答
2

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

数据大小无关紧要。

于 2009-11-13T17:41:10.283 回答
1

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

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

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

于 2009-11-13T17:37:41.727 回答
1

显然,答案是否定的,或者更确切地说,您对任何事情都没有任何保证。没有什么能阻止操作系统在启动打印出 100 字节结构的线程之前换出正在写入 100 字节结构的线程。

A memory barrier is used when you want to coordinate access to data through a flag or some other atomic value. I don't know what exactly you are trying to do, so I can't give you good example code about how you should do it.

于 2009-11-14T00:49:52.817 回答
0

好吧,首先你不应该有一个那么大的结构。除非你对如何使用结构非常小心,否则它会比使用类慢。此外,它与结构的值语义相悖。

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

volatile 关键字有点不同。它保证没有针对变量的操作被优化掉,并且确实保证了内存访问的顺序。但是,对于不能以原子方式访问的数据类型,它对于线程用途几乎没有用处,因为您仍然可以读取一半的新值和一半的旧值。

于 2009-11-13T17:45:57.470 回答