何时使用';挥发性';或';Thread.MemoryBarrier()和#x27;在线程安全锁定代码中?(C#)
什么时候应该使用volatile/Thread.MemoryBarrier()实现线程安全?有什么问题何时使用';挥发性';或';Thread.MemoryBarrier()和#x27;在线程安全锁定代码中?(C#),c#,.net,multithreading,volatile,C#,.net,Multithreading,Volatile,什么时候应该使用volatile/Thread.MemoryBarrier()实现线程安全?有什么问题 private static readonly object syncObj = new object(); private static int counter; public static int NextValue() { lock (syncObj) { return counter++; } } ? 这为您提供了所有必要的锁定、内存障碍等功
private static readonly object syncObj = new object();
private static int counter;
public static int NextValue()
{
lock (syncObj)
{
return counter++;
}
}
?
这为您提供了所有必要的锁定、内存障碍等功能。它比任何基于volatile
和Thread.MemoryBarrier()
的自定义同步代码都易于理解和阅读
编辑 我想不出哪种场景会使用
volatile
或Thread.MemoryBarrier()
。比如说
private static volatile int counter;
public static int NextValue()
{
return counter++;
}
不等同于上述代码,并且是不线程安全的(volatile
不会使+
神奇地变为线程安全)
在这种情况下:
private static volatile bool done;
void Thread1()
{
while (!done)
{
// do work
}
}
void Thread2()
{
// do work
done = true;
}
sum = 0;
foreach (int value in list) {
sum += value;
}
(这应该可以工作)我会在Thread2完成时使用a来发出信号。基本上,如果您正在使用任何其他类型的同步来使代码线程安全,那么您不需要这样做 大多数锁机制(包括锁)自动暗示内存屏障,以便多处理器可以获得正确的信息 Volatile和MemoryBarrier主要用于无锁场景,在这些场景中,您试图避免锁定带来的性能损失
编辑:你应该阅读Joe Duffy关于CLR 2.0内存模型的文章,它澄清了很多事情(如果你真的感兴趣,你应该阅读Joe Duffie的所有文章,他大体上是.NET中最擅长并行的人)你使用的
volatile
/Thread.MemoryBarrier()
当您希望在不锁定的情况下跨线程访问变量时
原子变量,例如
int
,总是一次完整地读取和写入。这意味着,在另一个线程更改之前,您永远不会得到该值的一半,而在另一个线程更改之后,您将永远不会得到该值的另一半。因此,您可以在不同的线程中安全地读取和写入值,而无需同步
但是,编译器可能会优化掉一些读写操作,而您可以使用volatile
关键字来阻止这些操作。例如,如果您有这样一个循环:
private static volatile bool done;
void Thread1()
{
while (!done)
{
// do work
}
}
void Thread2()
{
// do work
done = true;
}
sum = 0;
foreach (int value in list) {
sum += value;
}
编译器实际上可以在处理器寄存器中进行计算,并且只在循环后将值写入
sum
变量。如果将sum
变量设置为volatile
,编译器将为每次更改生成读取和写入变量的代码,这样它的值在整个循环中都是最新的。顾名思义,volatile保证将缓存值刷新到内存中,以便所有线程都看到相同的值。例如,如果有一个整数的最新写入保存在缓存中,其他线程可能看不到。他们甚至可以看到该整数的缓存副本。将变量标记为volatile可以直接从内存中读取
Sriwantha Sri Aravinda Attanayake您的代码没有“错误”。我试图掌握volatile&MemoryBarrier应用的环境。正如Jon Skeet在这里的回答:…net也有一个相当严格的内存模型,这样的读或写不能跟在另一个写之后。虽然ECMA规范有一个相当弱的内存模型,所以您可能需要注意这一点,请看,如果您只进行检索或赋值(说到第二个示例代码),因为它们是原子的。但是,是的,用途非常有限。我唯一能想到的地方是布尔菲尔德+1,但这是绝对清楚的。如果执行dtb所做的操作,则不需要volatile/Thread.MemoryBarrier。也有一些情况下,仅在成员上使用volatile是不够的。+1,但在哪些情况下,编译器会生成代码,为每次更改读取和写入变量,这很重要?这是在处理dtb时最需要的,如果没有一个线程得到与另一个线程不同的值,那么所有疯狂的事情都可能发生。好吧,这可能会使用内存障碍。当然,但我正在寻找一个真实的场景:)除了我回答中的最后一个例子之外,我想不出任何其他例子。“原子变量,例如int…”还有一个反例:设置长,x86计算机上的双精度或十进制变量不是线程安全的。编辑:参见底部的“螺纹安全”部分