开发者

Identify the thread which holds the lock

one of the threads in my application blocked at the following lock statement and resulted in a deadlock

void ExecuteCommand()
{
    lock(this._lockinstance)
    {
        // do some operation
    }
}

Is it possible to easily identify which thread is currently holding the lock?.. My application has mo开发者_JS百科re than 50 threads, which makes it difficult to go through each callstack using visual studio to locate the thread that holds the lock


Some sample code to try out:

class Test {
    private object locker = new object();
    public void Run() {
        lock (locker) {  // <== breakpoint here
            Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Set a breakpoint on the indicated line. When it breaks, use Debug + Windows + Memory + Memory 1. Right click the window and choose "4-byte Integer". In the Address box, type &locker. The 2nd word is the thread ID of the thread that owns the lock. Step past the lock statement to see it change.

Beware that the number is the managed thread ID, not the operating system thread ID that you see in the Debug + Windows + Threads window. That kinda sucks, you probably should add some logging to your program that dumps the value of ManagedThreadId so you have a way to match the value to a thread. Update: fixed in later VS versions, the Debug > Windows > Threads debugger window now shows the ManagedThreadId.


Recently I was trying to determine what function was holding a lock and found the following very useful and had not seen in demonstrated anywhere before. I've placed it as an answer here in case others find it useful too.

Many of the other solutions posted earlier require writing a new class and then converting of all lock(blah) to BetterLock(blah) which is a lot of work for debugging and which you may not want in the production/shipped version of your code. Others required having the debugger attached which changes the code's timing and could obscure the issue.

Instead, try the following...

Original code:

object obj = new object();
lock(obj)
{
    // Do stuff
}

Modified code for debugging:

object _obj = new object();
object obj
{
    get
    {
        System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(1);
        System.Diagnostics.Trace.WriteLine(String.Format("Lock acquired by: {0} on thread {1}", frame.GetMethod().Name, System.Threading.Thread.CurrentThread.ManagedThreadId));
        return _obj;
    }
}
// Note that the code within lock(obj) and the lock itself remain unchanged.
lock(obj)
{
    // Do stuff
}

By exposing obj as a property, at least temporarily, with very minimal code changes you can determine what function acquired the lock last and on what thread - just look at the Trace output for the last entry. Of course you can output any other information you might find useful in the getter as well.

No, this will not let you determine when a lock was released, but if it was getting released in a timely fashion, then you didn't actually have a lock contention issue in the first place.


You can implement a Monitor wrapper that saves stack traces & thread names on enter.

Old way:

private object myLock = new object();

...
lock(myLock)
{
    DoSomething();
}
...

With code below:

private SmartLock myLock = new SmartLock();

...
myLock.Lock( () =>
{
    DoSomething();
}
);
...

Source:

public class SmartLock
{
    private object LockObject = new object();
    private string HoldingTrace = "";

    private static int WARN_TIMEOUT_MS = 5000; //5 secs


    public void Lock(Action action)
    {
        try
        {
            Enter();
            action.Invoke();
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Lock action", ex);
        }
        finally
        {
            Exit();
        }

    }

    private void Enter()
    {
        try
        {
            bool locked = false;
            int timeoutMS = 0;
            while (!locked)
            {
                //keep trying to get the lock, and warn if not accessible after timeout
                locked = Monitor.TryEnter(LockObject, WARN_TIMEOUT_MS);
                if (!locked)
                {
                    timeoutMS += WARN_TIMEOUT_MS;
                    Globals.Warn("Lock held: " + (timeoutMS / 1000) + " secs by " + HoldingTrace + " requested by " + GetStackTrace());
                }
            }

            //save a stack trace for the code that is holding the lock
            HoldingTrace = GetStackTrace();
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Enter", ex);
        }
    }

    private string GetStackTrace()
    {
        StackTrace trace = new StackTrace();
        string threadID = Thread.CurrentThread.Name ?? "";
        return "[" + threadID + "]" + trace.ToString().Replace('\n', '|').Replace("\r", "");
    }

    private void Exit()
    {
        try
        {
            Monitor.Exit(LockObject);
            HoldingTrace = "";
        }
        catch (Exception ex)
        {
            Globals.Error("SmartLock Exit", ex);
        }
    }
}


Yes, there is a 'Threads' view that you can use in VS. Break anywhere in your application (or click the 'Break All' button) then you can select each thread and view who has the lock (if anyone).

To add it, go to Debug > Windows > Threads (Ctrl+D,T)


Old posts are old.

But i thought i might give a solution i find to be fairly useful for trying to track down dead locks and other locking problems.

I use a disposable class for my lock - I like Monitor but any locking mechanism could be used.

public class MonitorLock : IDisposable
{
    public static MonitorLock CreateLock(object value)
    {
        return new MonitorLock(value);
    }

    private readonly object _l;

    protected MonitorLock(object l)
    {
        _l = l;
        
        Console.WriteLine("Lock {0} attempt by {1}", _l, Thread.CurrentThread.ManagedThreadId);
        
        Monitor.Enter(_l);

        Console.WriteLine("Lock {0} held by {1}" , _l, Thread.CurrentThread.ManagedThreadId);
    }

    public void Dispose()
    {
        Monitor.Exit(_l);

        Console.WriteLine("Lock {0} released by {1}", _l, Thread.CurrentThread.ManagedThreadId);
    }
}

I use a lock object with a name so I can be clear as to which lock I'm trying to aquire.

public class LockObject
{
    public string Name { get; set; }

    public LockObject(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        return Name;
    }
}

Finally create a lock object, and then in a using block hold the object.

//create an object to lock on
private readonly object _requestLock = new LockObject("_requestLock");

using (MonitorLock.CreateLock(_requestLock))
{
    //do some work
}

Output should be something along the lines of

Lock _requestLock attempt by 92
Lock _requestLock held by 92
Lock _requestLock attempt by 19
Lock _requestLock released by 92
Lock _requestLock held by 19
Lock _requestLock released by 19

Hope that someone finds this useful :)


The Managed Stack Explorer from http://mse.codeplex.com/ or http://www.microsoft.com/downloadS/details.aspx?FamilyID=80cf81f7-d710-47e3-8b95-5a6555a230c2&displaylang=en is excellent in such cases.

It hooks into running managed code (appropriate permissions needed) including live code, and grabs a list of running threads. You can double-click on any of them or (more useful in cases like this) select the lot and hit enter for a quick relatively non-invasive (obviously it's going to consume resources, but it goes in and out as quickly as it can) dump of the current stacks of different threads. Great for finding a deadlock, infinite loop, near-infinite loop (for those times when your application accidentally depends upon astronomers being pessimistic about how long the earth will last to have a hope of completing) and other such cases.


I'm not sure in which version this feature was added, but the Visual Studio 2022 debugger now shows in its Call Stack window the ID of the thread that owns the lock on which another thread is waiting to acquire, e.g.,

Identify the thread which holds the lock

I found this over here.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜