In a multi-threaded application, for synchronization, the common coding format followed is ‘lock’. One of the major problems that multi-threaded applications suffer is ‘deadlock’ due to wrong implementation of lock statements. It’s a very difficult and tedious job to find out exactly where a dead lock happens. Although there are certain third party utilities, or you can take a dump and analyze the dead-lock problem, but unfortunately, it’s not a simple task, and moreover, it may happen that your application is in production and a dead-lock occurs causing the UI/activity to freeze, and the dump might not be taken at the user site.

This article explains how you can get proper diagnostic information in dead-lock scenarios so that programmers can fix such issues quickly and efficiently.

Also, it can trace a ‘lock’ activity. If multiple threads try to acquire a lock on the same object, only one thread at a time can acquire it, and all the other threads will be in a waiting state. This utility can identify the time for which a thread has been in waiting state for acquiring a lock on an object and the active state time (i.e., acquired the lock and is performing the activity). This analysis is useful if the wrong objects are locked for synchronization, which is hurting the performance of your application.

Example: Suppose the code contains four methods, M1(), M2(), M3(), and M4(). Each of these methods put a lock on the same object, say ‘obj’. But for synchronization, M1 and M2 needs to be synchronized (they share some common data), and M3 and M4 needs to be synchronized (M3 and M4 have different set of data shared between methods M3 and M4). But since the same object is used for the synchronization of ‘obj’, if one thread is executing M1() and another tries to execute M3(), that second thread will be in a waiting state until the lock in M1 is released, but as per the code, there is no need for synchronization of M1 and M3, and hence in this scenario, the lock that is put is not correct, it’s hurting the performance of the application.

With Monitor.Enter and Monitor.Exit, we acquire and release the lock on object 'obj', but it has one problem. What will happen if exception occurs in 'activity 1/2/3' and is not handled? In that case, Monitor.Exit will not execute, resulting in 'obj' in locked state only, which is a big problem. In the case of the 'lock' construct, upon exit of 'lock', the lock on the corresponding object is released. So, one solution can be to put Monitor.Exit in finally. But then, it's not a user friendly pattern compared to lock construct. So, the perfect solution for this is the 'using' construct. Upon exit of the ‘using’ statement, the Dispose() method gets called. So, we will put Monitor.Exit in the Dispose method. We can have code like this:

Here, ‘ThreadLock’ is a class that implements the IDisposable interface and ‘Lock’ is a static method that returns a new instance of ThreadLock. Upon exit of the ‘using’ statement, the Dispose() method gets called, where we remove the lock on the object (Monitor.Exit()).

Click the Using ‘ThreadLockTracer’ Utility button. it performs following code for the number of iterations specified in the textbox. In this case, it is 100000 (hundred thousand).

//Execute following line 100000 (hundred thousand) times
using (ThreadLock.Lock(objLockTest))
{
}

From the results, it is clear that the ‘Thread Lock Tracer’ utility is highly efficient; for hundred thousand iterations, it takes hardly 100 ms time extra, which should be acceptable, but in return, in case of trouble, it provides exclusive diagnostic information that is very much useful for fixing the problem.

Click the Generate Thread ‘Dead-Lock’ button. It generates a dead-lock between the threads.

Wait for around 3-5 seconds and then click the Scan ‘Dead-Lock’ button. It processes the thread locking information that is stored in the collection, and from that information, detects if a deadlock is there.

Using the concept explained in this article, tracing Thread-Deadlock functionality is highly efficient (for hundred thousand iterations, it takes around 100 ms only, this time may vary on your machine), and you can have it in your code. For this, we need to just search and replace the calls to ‘lock’ statements, and hence should not be too much of work.

Identifying Thread-Deadlock can be performed in a separate thread or at the application exit.

‘Tracing Thread Activity’ functionality is supposed to be used only in development environments. Tracing this information is costly in terms of performance, and hence it is suggested to switch it off in production scenarios.

Yes, sure till mostly we depend on dump as I have explained in the 'Introduction' section. Even I was also used to analyze the dump. But problem with dump approach is it have a lot of information, and you need to analyze it carefully. Whereas with this utility it can give the stack trace info of the threads that are part of dead-lock only, very specific information that can be easily analyzed by any developer.

Also it provides additional information regarding inefficient usage of 'lock' if any.