9

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 implmenting it.

webdad3
  • 8,893
  • 30
  • 121
  • 223
  • possible duplicate: http://stackoverflow.com/questions/9517951/using-interlocked-usage and http://stackoverflow.com/questions/154551/volatile-vs-interlocked-vs-lock – valverij May 07 '13 at 13:29
  • I saw that post but it skips ahead of my basic questions – webdad3 May 07 '13 at 13:30

3 Answers3

7

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 tricker 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. Intelocked offers ways to do updates with change-detection, and helper methods to do common operations like increment / decrement / add / etc.

Re 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.

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • In my example iCount is an Int32 – webdad3 May 07 '13 at 13:35
  • @JeffV yes, but IMO you need to understand that **this matters**, hence why I included it in the answer. It would be dangerous if I just said "yes, that's atomic" without that context, because you might change it to `long` in the real code. Equally, atomicity is only a *part* of the story here – Marc Gravell May 07 '13 at 13:38
  • Thank you for the clarification. I'm researching the lock(syncLock) construct now. I appreciate your answer. – webdad3 May 07 '13 at 13:41
  • If I'm inserting into a database table should I put a lock around that as well? – webdad3 May 07 '13 at 18:30
  • @Jeff complex question - databases have their own locking constructs. Also keep in mind that a database lock is cross-process (and cross-machine), whereas "lock" is single-process. – Marc Gravell May 07 '13 at 18:36
  • the reason why I ask is that there is code that reads and manipulates iCount with a InsertOnSubmit command. I've noticed that putting the lock around that adds significant time to the process – webdad3 May 07 '13 at 18:42
  • @Jeff without a more complete overview of the scenario, I can't comment on that other than to say "oh" (and maybe raise an eyebrow quizically) – Marc Gravell May 07 '13 at 18:45
1

When doing multithreading over shared, mutable state you need to synchronize. You do not need to use Interlocked. Interlocked is for advanced users. I suggest you use the lock C# statement and only use Interlocked for easy cases (increment a shared counter) or performance critical cases.

Interlocked can only be used to access a single variable at a time and only quite primitive operations are supported. You will have a really hard time synchronizing multiple variables with Interlocked.

In your example, nobody can tell whether you need to synchronize or not because thread safety is a property of the whole program, not of a single statement or function. You need to regard all code operating on the shared state as a whole.

usr
  • 168,620
  • 35
  • 240
  • 369
0

I would like to add to the other answers that Microsoft has additionally introduced ImmutableInterlocked class.

This class is designed for handling Immutable Collections. The class has a set of functions for updating immutable collections using Compare-And-Swap pattern.

You can find it in the System.Collections.Immutable namespace.

https://msdn.microsoft.com/en-us/library/system.collections.immutable.immutableinterlocked(v=vs.111).aspx

V. S.
  • 1,086
  • 14
  • 14