Understanding non blocking thread synchronization and Thread.MemoryBarrier
In this threading online book: http://www.albahari.com/threading/part4.aspx
theres an example of Thread.MemoryBarrier()
class Foo
{
int _answer;
bool _complete;
void A()
{
_answer = 123;
Thread.MemoryBarrier(); // Barrier 1
_complete = true;
Thread.MemoryBarrier(); // Barrier 2
}
void B()
{
Thread.MemoryBarrier(); // Barrier 3
if (_complete)
{
Thread.MemoryBarrier(); // Barrier 4
Console.WriteLine (_answer);
}
}
}
We got a discussion whether there is any thread blocking going on or not?
Im thinking there is some, especially given that
A full fence takes around ten nanoseconds on a 2010-era desktop.
On other hand, full fence is only supposed to disable instructions reodering and caching
which by its sound doesn't qualify as thread blocking, (unlike lock
where its clear that thread waits for other to release lock before it continues, and is blocked during that time)
About that thread 'block state'. im talking not开发者_JS百科 in terms of whether thread is put into blocked state or not, but whether there is some thread synchronization happening, which means one thread is not able to run while other isn't letting it to do so, by means of MemoryBarrier in this case.
Also Id like to get clear understanding what each barrier achieves. For example Barrier 2 - how exactly it provides freshness guarantee and how is it connected to barrier 3? If someone would explain in detail whats each barrier purpose here( what could possibly go wrong if 1 or 2 or 3 or 4 weren't there) I think id improve my understanding of this greatly.
EDIT: its mostly clear now what 1, 2, and 3 do. However what 4 does that 3 doesn't is still unclear.
The fact that instructions take time to execute does not imply that a thread is blocked. A thread is blocked when it is specifically put into a blocked state, which MemoryBarrier()
does not do.
The processor instructions that actually prevent instruction reordering and cache flushing take time, because they must wait for the caches to become coherent again. During that time, the thread is still considered running.
Update: So let's take a look at what's actually happening in the example, and what each memory barrier actually does.
As the link says, 1 and 4 ensure that the correct answers are produced. That's because 1 ensures that the answers are flushed into memory, and 4 ensures that the read caches are flushed prior to retrieving the variables.
2 and 3 ensure that if A
runs first, then B
will always print the answers. Barrier 2 ensures that the write of true
is flushed to memory, and barrier 3 ensures that the read cahces are flushed before testing _complete
's value.
The cache and memory flushing should be clear enough, so let's look at instruction reordering. The way the compiler, CLR and CPU know they can reorder instructions is by analyzing a set of instructions in sequence. When they see the barrier instruction in the middle of a sequence, they know that instructions can't move across that boundary. That ensures that in addition to cache freshness, the instructions occur in the correct order.
精彩评论