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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With