简体   繁体   中英

Is volatile needed with indirect access?

Please note I am not asking about replacing volatile with other means (like lock), I am asking about volatile nature -- thus I use "needed" in sense, volatile or no volatile .

Consider such case that one thread only write to variable x ( Int32 ) and the other thread only reads it. The access in both cases is direct.

The volatile is needed to avoid caching, correct?

But what if the access to x is indirect -- for example via property:

int x;
int access_x { get { return x; } set { x = value; } } 

So both threads now uses only access_x , not x . Is x needed to be marked as volatile ? If yes is there some limit of indirection when volatile is not needed anymore?

Update : consider such code of the reader (no writes):

if (x>10)
   ...
// second thread changes `x`
if (x>10)
  ...

In the second if compiler could evaluate use the old value because x could be in cache and without volatile there is no need to refetch. My question is about such change:

if (access_x>10)
   ...   
// second thread changes `x`
if (access_x>10)
  ...

And let's say I skip volatile for x . What will happen / what can happen?

Is x needed to be marked as volatile ?

Yes, and no (but mostly yes).

"Yes", because technically you have no guarantees here. The compilers (C# and JIT) are permitted to make any optimization they see fit, as long as the optimization would not change the behavior of the code when executed in a single-thread. One obvious optimization is to omit the call to the property setter and getter and just directly access the field (ie inlining). Of course, the compiler is allowed to do whatever analysis it wants and make further optimizations.

"No", because in practice this usually is not a problem. With the access wrapped in a method, the C# compiler won't optimize away the field, and the JIT compiler is unlikely to do so (even if the methods are inlined…again, no guarantees, but AFAIK such an optimization isn't performed, and I think it not likely a future version would). So all you're left with is memory coherency issues (the other reason volatile is used…ie essentially dealing with optimizations performed at the hardware level).

As long as your code is only ever going to run on Intel x86-compatible hardware, that hardware treats all reads and writes as volatile.

However, other platforms may be different. Itanium and ARM being a couple of common examples which have different memory models.

Personally, I prefer to write to the technicalities. Writing code that just happens to work, in spite of a lack of guarantee, is just asking to find some time in the future that the code stops working for some mysterious reason.

So IMHO you really should mark the field as volatile .

If yes is there some limit of indirection when volatile is not needed anymore?

No. If such a limit existed, it would have to be documented to be useful. In practice, the limit in terms of compiler optimizations is really just a "level of indirection" (as you put it). But no amount of levels of indirection avoid the hardware-level optimizations, and even the limit in terms of compiler optimizations is strictly "in practice". You have no guarantee that the compiler will never analyze the code more deeply and perform more aggressive optimizations, to any arbitrarily deep level of calls.


More generally, my rule of thumb is this: if I am trying to decide whether I should use some particular feature that I know is normally used to protect against bugs in concurrency-related scenarios, and I have to ask "do I really need this feature, or will the code work fine without it?", then I probably don't know enough about the way that feature works for me to safely avoid using it.

Consider it a variation on the old "if you have to ask how much it costs, you can't afford it."

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