44

The following code

using System.Threading;

class Test
{
    volatile int counter = 0;
    public void Increment()
    {
        Interlocked.Increment(ref counter);
    }
}

Raises the following compiler warning:

"A reference to a volatile field will not be treated as volatile"

Am I doing something wrong here to raise this warning? Why does the compiler me warn about this?

Jader Dias
  • 88,211
  • 155
  • 421
  • 625

4 Answers4

47

You are not doing anything wrong. According to the documentation:

A volatile field should not normally be passed using a ref or out parameter, since it will not be treated as volatile within the scope of the function. There are exceptions to this, such as when calling an interlocked API.

Tjaart
  • 3,912
  • 2
  • 37
  • 61
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 20
    I'd say that the Interlocked methods still aren't treating it any differently to how they'd treat any other field - it's just that *all* fields are handled in a special way by Interlocked. – Jon Skeet Jan 08 '09 at 17:32
  • 3
    Agreed. Volatile is irrelevant to the Interlocked API. But in certain cases, it's very useful to mix Interlocked and volatile. – IamIC Feb 13 '13 at 22:46
  • a bit late to the party, but @IamIC : care to elaborate which cases would those be? – jajdoo May 29 '16 at 06:59
  • 1
    @jajdoo in a nutshell, volatile merely stops compiler and CPU optimizations on values that could prevent threads/cores from seeing the latest value. So it's a mandatory attribute for values that are accessed from multiple threads. However, volatile does not provide atomic operations. For that, we need Interlocked. We could use only Interlocked and not volatile, which is what .Net is looking for. But on x86 that is less efficient for reads, so mixing the two is optimum. On Itanium and ARM, we must always use Interlocked. – IamIC May 29 '16 at 07:46
35

Basically the warning is that when you pass a volatile field by reference, the calling code doesn't know to treat it in a volatile manner. For Interlocked.Increment that probably doesn't matter, due to the nature of the method - but then you don't need the variable to be volatile anyway if you're using Interlocked.

In general, I think I'd avoid mixing the two - if you're using Interlocked, do it everywhere (using Interlocked.CompareExchange(ref counter, 0, 0) to read it). I can't say I use volatile very often, personally. For simple counters I might use Interlocked, but I'm more likely to use a lock for most tasks.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • You are getting me confused. In a previous question (395232) I understood from your answer that I needed both Interlocked and volatile. Now you say "you don't need the variable to be volatile anyway if you're using Interlocked." – Jader Dias Jan 08 '09 at 17:33
  • No, they're alternatives to each other: "Or preferably use Interlocked, a volatile variable, or a lock." That was meant to give three *different* approaches, not a combined one. Was that the part which was misleading you? – Jon Skeet Jan 08 '09 at 17:52
  • I understand that in a multiprocessor enviroment, multiple caches can make one processor increment one cached value that isn't the last value. In that case, as far as I know, a Interlocked won't help, cause it does not creates the MemoryBarrier that is needed to refresh the caches. – Jader Dias Jan 09 '09 at 02:25
  • 1
    One question, do you visit every question you answered to check if anyone has commented, or you have some sort of RSS that alerts you? – Jader Dias Jan 09 '09 at 02:26
  • 3
    Interlocked does everything required to make it right - memory barriers, atomicity etc. It would be useless without this. – Jon Skeet Jan 09 '09 at 11:52
  • @JonSkeet I've found that mixing Interlocked and volatile is useful for things like linked lists, where a CAS is needed for the read move next, but volatile is fine for write create next. Also, I wouldn't position Interlocked and volatile as alternatives since they serve totally different purposes. – IamIC Feb 13 '13 at 22:42
  • @IanC: I'd say they have *subtly* different purposes - but totally? I think that's a stretch. – Jon Skeet Feb 13 '13 at 23:21
  • @JonSkeet What I mean is Interlocked is designed to ensure operations are atomic. Volatile is designed to ensure variables aren't cached and are "fresh". Although Interlocked does this too, their purposes are different. – IamIC Feb 13 '13 at 23:23
  • 1
    @IanC: How about Interlocked.Read, Interlocked.MemoryBarrier? I'd also argue that the exact guaranteed behaviour of volatile is sufficiently subtle that it's not appropriate for most developers to use (including myself). – Jon Skeet Feb 14 '13 at 06:47
  • 1
    @JonSkeet I agree that volatile is, well, volatile :). The fact it can't be applied generally to 64 bit variables alone is almost sufficient to disregard it, IMO. I use it exclusively for freshness for 32 bit variables & objects, and understand this applies only to the variables volatile is applied to. It has no other guarantees as far as I'm concerned, such as preventing write/read reordering (cf. Albahari). For that, Interlocked / MemoryBarrier / lock. Interlocked.Read & Thread.VolatileRead also serve to guarantee freshness, as does volatile. I understand what you're saying in this regard. – IamIC Feb 14 '13 at 14:03
31

Use this:

#pragma warning disable 420
if(Interlocked.CompareExchange(ref isLoaded, 1, 0) != 0)
    return;
#pragma warning restore 420
STA
  • 30,729
  • 8
  • 45
  • 59
Robert Fraser
  • 10,649
  • 8
  • 69
  • 93
  • 4
    But it does come in handy once you understand the other answers, should you decide to use both Interlocked and volatile. – yoyo Feb 05 '15 at 00:05
  • 5
    @JaderDias -- No, but it tells you what to DO about the warning. Which is super useful. -- Also the artwork was kind of funny too. – BrainSlugs83 Oct 01 '15 at 02:43
  • @RyanM the artwork was funny. Why did you remove it? This answer looks naked without it! – Theodor Zoulias Feb 10 '21 at 10:29
3

You're getting the error because you're passing the field by reference. I think what this means is that the target method has no idea the field is marked as volatile, and therefore will not treat it as such.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393