问题
Lets say we have a class like so:
public class Foo
{
private Bar bar = new Bar();
public void DoStuffInThread1()
{
var old = Interlocked.Exchange(ref bar,new Bar());
//do things with old
//everything is fine here, I'm sure I have the previous bar value
}
public void OtherStuffFromThread2()
{
//how do I ensure that I have the latest bar ref here
//considering mem cahces etc
bar.Something();
}
}
And lets say we have two threads, one operating on DoStuffInThread1
and another on OtherStuffFromThread2
.
How do I ensure that thread2 always sees the latest bar
?
volatile doesn't help. and I don't want old school locks.
There has to be a way to read the correct value of bar with mem barriers/interlocked somehow?
回答1:
You are missing the point...
Unless you do:
public void OtherStuffFromThread2()
{
while (true)
{
//how do I ensure that I have the latest bar ref here
//considering mem cahces etc
bar.Something();
}
}
That is something quite improbable, nearly every method you could use on OtherStuffFromThread2()
to wait for thread1 to be ready will cause an implicit memorybarrier... See for example Memory barrier generators some constructs that cause memorybarriers...
So:
public void OtherStuffFromThread2()
{
Thread.Sleep(Timespan.FromMinutes(1));
// Implicit MemoryBarrier here :-)
//how do I ensure that I have the latest bar ref here
//considering mem cahces etc
bar.Something();
}
If you really want to read the value of a variable, you can read a volatile variable and then read your variable (or read the same volatile variable twice). Why? because a volatile read causes an acquire semantics, which means it can’t be reordered with subsequent memory operations, see https://msdn.microsoft.com/en-us/library/aa645755(v=vs.71).aspx :
A read of a volatile field is called a volatile read. A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.
So if you do:
private static int myuselessvolatilefieldthatcanbestatic;
private int thefieldiwanttoread;
and then
var useless = myuselessvolatilefieldthatcanbestatic;
var iwanttoknow = thefieldiwanttoread;
The thefieldiwanttoread
will contain a value that will be read after a new read to myuselessvolatilefieldthatcanbestatic
has been done.
Note that without a synchronization primitive, it will be difficult to know when the myuselessvolatilefieldthatcanbestatic
will be done :-), but:
while (true)
{
var useless = myuselessvolatilefieldthatcanbestatic;
var iwanttoknow = thefieldiwanttoread;
// Some code goes here
}
Now at least you can use your while (true)
:-)
回答2:
If you want to read the latest available value, you should use something like Interlocked.CompareExchange(ref bar, null, null)
. Checking for null is just for satisfying CompareExchange signature (if bar
is null
, then it will set it to null
). This will give you the latest value that was available among CPUs at the execution moment.
来源:https://stackoverflow.com/questions/29107314/lockfree-read-value-after-interlocked-exchange