简体   繁体   中英

Lockfree Read value after Interlocked.Exchange?

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?

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) :-)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM