Question

I've been doing simple multi-threading in VB.NET for a while, and have just gotten into my first large multi-threaded project. I've always done everything using the Synclock statement because I didn't think there was a better way.

I just learned about the Interlocked Class - it makes it look as though all this:

Private SomeInt as Integer
Private SomeInt_LockObject as New Object

Public Sub IntrementSomeInt
    Synclock SomeInt_LockObject
        SomeInt += 1
    End Synclock
End Sub

Can be replaced with a single statement:

Interlocked.Increment(SomeInt)

This handles all the locking internally and modifies the number. This would be much simpler than writing my own locks for simple operations (longer-running or more complicated operations obviously still need their own locking).

Is there a reason why I'd rolling my own locking, using dedicated locking objects, when I can accomplish the same thing using the Interlocked methods?

Was it helpful?

Solution

You're correct; Interlocked should be used here, and will be faster than SyncLock.
However, the Interlocked class is not well-known.

However, there are situations where you need to use SyncLock and Interlocked will not help.

OTHER TIPS

This is because no one knows about it. Spread the word!

The short answer is because using a Monitor lock (SyncLock in VB and lock { } in C#) not only assures that only one thread at a time can access the variable (or, in a strict sense, only one thread at a time can obtain a lock on the locking object), but it also creates the memory barrier required to ensure that reads on the variable aren't optimized away.

If you're never simply reading the value of the variable (in other words, all of your work is done through calls to Interlocked), then you'll be OK. However, if you need to be able to perform a normal read of the variable then the situation is more complicated. Lockless reads/writes are usually accomplished in C# using the volatile keyword. This instructs the compiler to read the value of the variable everywhere it's used, rather than optimizing away any of these reads into a local cache. Unfortunately there is no equivalent in VB.NET, so you'll have to use something else.

The accepted answer to this question should provide some more information on what you can do. In short, most people use SyncLock in VB.NET because it's easier and less complicated than the logic required to do it without SyncLock.

I once read a very good explanation on so called non-atomic and atomic (in VB: interlocked) operations and will try to sum that up.

Normal "non-atomic" operations consist of several steps 

-> other threads can work in between those streps

"Atomic" operations consist of one only one step 

-> other threads cannot perform work while atomic operations are processed, atomic operations are always processed as whole

The interlocked class is a collection of such atomic operations and therefor threadsafe by definition. Even with multiple threads performing read and write operations on the same variable, these operations are absolutely thread safe.

Still a combination of those threadsafe commands can be unsafe, as race conditions can occur in between the atomic operations.

So if you want to for example compare 2 variables and then increment the smaller one, this is not thread safe even though the single operations for themself are (interlocked.compare, interlocked.increment). Here you still have to use synclocks.

Other than that limitation there is no "hidden bad side" of interlocked.

One example for a racecondition with a = 5:

 Thread1: a+=1
 Thread2: a+=2    
 --> supposed to be 8, but can be only 6 or 7,
 but can also be 6 or 7 depending on which thread wins the race

option 1:

T1 step 1: read 5
T1 step 2: add 1 = 6
T1 step 3: write 6
T2 step 1: read 6
T2 step 2: add 2 = 8
T2 step 3: write 8
--> is 8 as supposed

or option 2:

T1 step 1: read 5
T2 step 1: read 5
T1 step 2: add 1 = 6
T2 step 2: add 2 = 7
T2 step 3: write 7
T1 step 3: write 6
--> is only 6

or option 3:

T1 step 1: read 5
T2 step 1: read 5
T1 step 2: add 1 = 6
T2 step 2: add 2 = 7
T1 step 3: write 6
T2 step 3: write 7
--> is only 7

With interlocked.increment:

option 1:

T1 step 1: read 5, add 1, write 6
T2 step 1: read 6, add 2, write 8

or option 2:

T2 step 1: read 5, add 2, write 7
T1 step 1: read 7, add 1, write 8

-> in all cases a = 8 as supposed, threadsafe solution

All the questions that were posted here can be solved by applying this simple example to the questionable code.

Hope this helps other people who google this topic. Janis

Interlocked is limited to simple operations on Integer, Long and Boolean and such.

If you want to add an item to a shared List(of T) for example, you will still need SynClock.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top