Is common in new or not enough experienced developers having problems with a misuse of dotnet lock statement.
When you develop multithreading process is common to use the lock statement for avoid resources access conflicts.
The lock statement has a simple syntax, however, this simplicity and ubiquity have a downside.. if you dont use it correctly maybe you have a the misintended effect.
Without discuss the more complex forms of thread coordination, the basic use of lock have several gotchas.
With this simple guide you can be awared about lock misuses:
-
Locking reference type:
The first and common mistake is to try to lock on a variable that refers to a value type rather than an object type. For example:
static int myLock; static void WriteToResource() { lock (myLock) { ... } }
But the compiler fails, and throw an error telling you that “int” is not a reference type.
Also if then you try this (thinking that Monitor accepts reference type):static int myLock; static void WriteToResource() { Monitor.Enter(myLock); try { ... } finally { Monitor.Exit(myLock); } }
You receive at runtime a
SynchronizationLockException
Because lock statement causes that compiler check some other staff that dont do with Monitor and also at runtime C# allow the automatic boxing wrapping value types to reference type. So… why Monitor fails? because each time the language do the boxing create a new object to wrap the two variables… oh! if you have different object instance, the Monitor not lock anything!
-
Prevent to lock anything with public access:
Assume that you instantiate a public property or a singleton static public variable of a class to use in some lock. For example, a stream that fills with information and then writes to a file. Surely it works for you, but then another developer uses the same object to do the same for another case of class feature… here we find another problem.
As result of the second developer, now we have two process locking a same object for different threads that have nothing related. This produces inefficiency and a difficult issue or a bug to troubleshoot.
A common other bug related to public access is when the developers use this:lock (this) { ... }
“this” instance is public! and in this case any thread related to the class use the same object to lock and not have effect.
-
Check locking states:
Is common to check if the lock is already in use or the thread ends to allow the next thread. So, you may need to validate some staff that you recently evaluated before the lock statement. Is common to use boolean variables to check the state and prevent to do the same staff and check in each thread locking.
-
Avoid lot of lockings:
A lot of lockings or lock inside another locks cause a high use of resources. And also, maybe you add a lock without sense. For example, is common to use a lock in a statement that has other methods of our or dotnet framework classes with locks inside, enumerable classes and file access classes also have locking in some methods, so is not necessary to lock our method that call this method. This classes or methods called: “thread-safe”. Is good practice to consider or read dotnet documentation to know about which classes are “thread-safe”.
-
To start and use in these simple cases:
always use:
private static object lockOne = new object();
And use other locking object for each related locking.
With this simple best practices you can start to prevent issues and bugs with multithreading and asynchronous
context.
Then I will write about more complex cases or I invite you to look for good practices in asynchronous and
multi-threaded contexts.