阅读Joseph Albahari的线程教程,以下内容被提及为内存障碍的生成器:
lock陈述(Monitor.Enter/ Monitor.Exit)Interlocked班上的所有方法此外,Hans Passant和Brian Gideon 补充了以下内容(假设其中没有一个已经符合以前的类别之一):
Thread.Sleep()我想知道这个清单是否完整(如果完整清单甚至可以实际制作)
编辑补充建议:
最近我在WebSite中看到了这段代码,我的问题如下:
private bool mbTestFinished = false;
private bool IsFinished()
{
lock( mLock )
{
return mbTestFinished;
}
}
internal void SetFinished()
{
lock( mLock )
{
mbTestFinished = true;
}
}
Run Code Online (Sandbox Code Playgroud)
在多线程环境中,是否真的有必要锁定访问权限mbTestFinished?
像许多其他人一样,我一直对易失性读/写和围栏感到困惑.所以现在我想完全理解这些是做什么的.
因此,易失性读取应该(1)表现出获取语义,(2)保证读取的值是新鲜的,即它不是缓存值.让我们关注(2).
现在,我已经读过,如果你想执行一个易失性读取,你应该在读取后引入一个获取栅栏(或一个完整的栅栏),如下所示:
int local = shared;
Thread.MemoryBarrier();
Run Code Online (Sandbox Code Playgroud)
这究竟是如何阻止读取操作使用以前缓存的值?根据栅栏的定义(不允许读取/存储在栅栏上方/下方移动),我会在读取之前插入栅栏,防止读取穿过栅栏并及时向后移动(也就是说,是缓存).
如何防止读取被及时向前移动(或后续指令被及时向后移动)保证了易失性(新鲜)读取?它有什么用?
类似地,我认为易失性写入应该在写入操作之后引入栅栏,从而阻止处理器及时向前移动写入(也就是说,延迟写入).我相信这会使处理器刷新对主存储器的写入.
但令我惊讶的是,C#实现在写入之前引入了栅栏!
[MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
public static void VolatileWrite(ref int address, int value)
{
MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
address = value;
}
Run Code Online (Sandbox Code Playgroud)
更新
根据这个例子,显然是从"坚果壳中的C#4"中取出的,在写入之后放置的栅栏2 应该强制写入立即刷新到主存储器,并且在读取之前放置的栅栏3 应该保证一个新的阅读:
class Foo{
int _answer;
bool complete;
void A(){
_answer = 123;
Thread.MemoryBarrier(); …Run Code Online (Sandbox Code Playgroud) 这是一个简单的问题,但在阅读之后我为什么需要内存屏障?我很困惑.
在下面的示例中,假设不同的线程重复调用Increment和Counter:
class Foo{
int _counter=0;
public int Counter
{
get { return _counter; }
}
public void Increment()
{
Interlocked.Increment(ref _counter);
}
}
Run Code Online (Sandbox Code Playgroud)
对不起,如果我误解了为什么我需要内存屏障?但似乎这表明上述课程在阅读_counter的价值时可能无法提供新鲜度保证.重复访问Counter属性的线程是否会永远停留在旧的Counter值(因为它缓存在寄存器中)?
在return _counter;必要之前是内存屏障还是锁?
c# multithreading thread-safety shared-memory memory-barriers