I would start by sending trace output every time a thread is about to enter/leave a critical section as well as every time it successfully acquires a lock. Use the System.Diagnostics.Trace
class.
Then you should be able to determine from your trace output which thread actually has the lock.
Typical trace code:
Trace.WriteLine("Acquiring lock - foo");
lock (foo)
{
Trace.WriteLine("Acquired lock - foo");
// Do some stuff
Trace.WriteLine("Releasing lock - foo");
}
Trace.WriteLine("Released lock - foo");
Depending on the way your program is structured, this might not be useful unless you include thread information in the trace output, for example:
Trace.WriteLine(string.Format("Thread {0} - Acquiring lock - foo",
Thread.CurrentThread.ManagedThreadId);
Once you've figured out which thread has the lock, you can go into the debugger and see which lock it's waiting for, then use the same trace output to figure out who has the other lock. In most cases there will be two threads participating in a deadlock and this will let you find both of them.