Search code examples
c#multithreadinginterlocked

Interlocked - when do I use it?


I just found out about the Interlocked class and now I have some basic questions.

From my understanding I should use Interlocked when manipulating numeric variables when multi-threading. If that statement is true, what about doing reads or just general using of the variables?

For example:

if ((iCount % 100) == 0)

Do I need to use an Interlocked statement there?

What about when I'm initializing the variable:

Int32 iCount = 0;

I need to make sure I understand this before implementing it.


Solution

  • There are various factors here, but principally volatility and atomicity. In your statement:

    if ((iCount % 100) == 0)
    

    Do I need to use an Interlocked statement there?

    we first need to ask "what is iCount?". If it is long / ulong, then it is not guaranteed to be atomic, so you absolutely need some kind of protection (such as via Interlocked) to avoid getting a "torn" value (reading it half-way through being updated, giving a phantom value that it never actually was - for example, changing from 00000000 to FFFFFFFF you could read 0000FFFF or FFFF0000). If it is an int, it will be guaranteed atomic. The next question is: do I need to see updates? The CPU has various levels of caching built in, and code that appears to read from a field can end up actually just reading from a local register or cache - and never touching the actual memory. If that is a risk, then you can mitigate that by using Interlocked, although in many cases using volatile will also guard against this.

    The third and trickier question comes when discussing updates: do you want "last edit blindly wins"? if so, just update the value (presumably using volatile to allow reads) - however - there is a risk of lost updates if two threads are editing. As an example, if two threads each increment and decrement at the same time the final value could be 0 or 1 - not necessarily what you wanted. Interlocked offers ways to do updates with change-detection, and helper methods to do common operations like increment / decrement / add / etc.

    Regarding your other question:

    What about when I'm initializing the variable:

    Int32 iCount = 0;
    

    Field-initializers are only executed on one thread, so do not need additional protection - that is fine.


    However! Threading is hard. If you are at all unsure, keep it simple: use lock. For example (assuming you want per-instance synchronisation):

    private int iCount = 0;
    private readonly object syncLock = new object();
    ...
    lock(syncLock) {
        // code that reads or manipulates iCount
    }
    

    In many cases, this works fine.