![]() |
General Programming »
Threads, Processes & IPC »
Multi-threading
Intermediate
License: The Code Project Open License (CPOL)
Multi-Threading: Deadlock Tracer UtilityBy Chivate AtulTracing dead-locks in multi-threaded applications. |
C# (C# 1.0, C# 2.0, C# 3.0), Windows (Win2K, WinXP, Win2003, Vista), .NET (.NET 1.0, .NET 1.1, .NET 2.0, .NET 3.0, .NET 3.5), WinForms, WebForms, Dev
|
|
Advanced Search |
|
|
||||||||||||||||||
Below are the topics grouped by relationship to each other:
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.
Inline code/ideas that can detect dead-lock scenarios because of improper implementation of ‘lock’s, and can provide helpful diagnostic information to trace deadlocks.
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 (they share common data). 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.
Here is a general ‘lock’ statement, in which a thread acquires a lock on object ‘obj’, and once the scope of the ‘lock’ ends, it releases the lock on that object.
lock(obj)
{
// perform activity 1
// perform activity 2
// perform activity 3
}
This code is similar to:
Monitor.Enter(obj); //acquire lock on object
// perform activity 1
// perform activity 2
// perform activity 3
Monitor.Exit(obj); //release lock
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:
using(ThreadLock.Lock(obj))
{
// perform activity 1
// perform activity 2
// perform activity 3
}
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()).
public static ThreadLock Lock(object objLock)
{
return new ThreadLock(objLock);
}
public ThreadLock(object objLock)
{
this.status = Status.Acquiring; //useful for detecting dead-lock
this.objLock = objLock;
//collect useful information about the context such
//as stacktrace, time to acquire the lock(T1)
Monitor.Enter(objLock);
this.status = Status.Acquired;
//lock is acuired, so collect acquired-time(T2)
//[T2-T1 = time taken to acquire lock]
}
public void Dispose()
{
Monitor.Exit(objLock);
//T3: activity in a lock is over
//Serialize this class for doing analysis of thread-lock activity time
}
Here is the main screen of the sample application:
Click the Regular ‘lock’ test button. It performs the 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
lock (objLockTest)
{
}
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.
lock’ statements, and hence should not be too much of work.lock’ usage is very easy and simple, and makes the life of developers easy.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Nov 2008 Editor: Smitha Vijayan |
Copyright 2008 by Chivate Atul Everything else Copyright © CodeProject, 1999-2009 Web10 | Advertise on the Code Project |