vb.Net multithreading question:
What is the difference between
SyncLock syncRoot
''# Do Stuff
End SyncLock
-and-
SyncLock Me
''# Do Stuff
End SyncLock
vb.Net multithreading question:
What is the difference between
SyncLock syncRoot
''# Do Stuff
End SyncLock
-and-
SyncLock Me
''# Do Stuff
End SyncLock
You're locking on different objects.
If other parts of your code (or internal code) sync on SyncRoot
, which they should, then you're breaking things (i.e. introducing threading bugs) by syncing on Me
.
You should definitely sync on SyncRoot
--that's why it's there.
All code that happens within a SyncLock
block is synchronized with all other code happening within a SyncLock
block on the same object. Obviously, Me
is not the same as syncRoot
(which is, I'm assuming, Me.SyncRoot
, if your Me
is an ICollection
).
Code happening within a SyncLock
block on one object is not going to be synchronized with code within a SyncLock
block on a different object.
Say you have this code:
' happening on thread 1 '
SyncLock myColl.SyncRoot
myColl.Add(myObject)
End SyncLock
' happening on thread 2 '
SyncLock myColl.SyncRoot
myColl.Remove(myObject)
End SyncLock
The above is fine: the Add
and Remove
calls are synchronized, meaning they will not occur simultaneously (whichever gets called first will execute, and the second will not execute until the first is finished).
But suppose you had this instead:
' happening on thread 1 '
SyncLock myColl.SyncRoot
myColl.Add(myObject)
End SyncLock
' happening on thread 2 '
SyncLock myColl ' NOTE: SyncLock on a different object '
myColl.Remove(myObject)
End SyncLock
The above Add
and Remove
calls are not synchronized in any way, shape, or form. Thus there is no thread safety in the above code.
Now, why does SyncRoot
exist? Quite simply, because it makes sense to synchronize on the smallest scale necessary; i.e., there is no need to synchronize code that doesn't actually need to be synchronized.
Consider this example:
' happening on thread 1 '
SyncLock myColl
myColl.Add(myObject)
End SyncLock
' happening on thread 2 '
SyncLock myColl
' Why you would have code like this, I do not know; '
' this is just for illustration. '
myColl.Name = myColl.Name.Replace("Joe", "Bill")
End SyncLock
' happening on thread 3 '
SyncLock myColl
myColl.Name = myColl.Name.Replace("Bill", "Joe")
End SyncLock
In the above, you are synchronizing more than necessary. The Add
call really has nothing to do with the renaming of the myColl
object; thus the code does not need to be synchronized.
This is the idea behind the SyncRoot
property: it gives you an object whose entire purpose is to provide a common object with which modifications to/enumerations of the collection can be synchronized. Code that involves the collection in some other way -- but which does not need to be synchronized with code that modifies or reads the contents of the collection -- should be synchronized, where appropriate, on a different object.
If Object.ReferenceEquals(syncRoot, Me) = True
then nothing is different. Otherwise the lock is acquired using different objects.
If your use of syncRoot
is equivalent to ICollection.SyncRoot
then the lock will be acquired using the same object that is used internally by the collection for its own locking. This allows you to synchronize access to enumerators. For example:
SyncLock collection.SyncRoot
For Each item As Object in collection
Next
End SyncLock
As a matter of convention .NET developers avoid using Me
as the lock object. This is especially true if Me
references as object that is visible to the public API of a class library. The reason we avoid this is because other code may use the same object to acquire a lock for a reason that conflicts with the semantic behavior you are trying to accomplish in your code. This conflict can lead to bottlenecks or even deadlocks.
It should be noted that SyncLock
does not synchronize access to the lock object itself, but rather the code wrapped by the SyncLock
construct. In other words, code that is guarded by a SyncLock
using the same object is effectively serialized.