识别持有锁的线程

Maa*_*anu 35 c#

我的应用程序中的一个线程在以下锁定语句中被阻塞并导致死锁

void ExecuteCommand()
{
    lock(this._lockinstance)
    {
        // do some operation
    }
}
Run Code Online (Sandbox Code Playgroud)

是否有可能轻松识别当前持有锁的哪个线程?..我的应用程序有超过50个线程,这使得使用visual studio查找持有锁的线程很难通过每个callstack

Han*_*ant 19

尝试一些示例代码:

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

在指示的行上设置断点.当它中断时,使用Debug + Windows + Memory + Memory 1.右键单击窗口并选择"4-byte Integer".在"地址"框中,键入&locker.第二个字是拥有锁的线程的线程ID.跳过锁定语句以查看其更改.

请注意,该数字是托管线程ID,而不是您在Debug + Windows + Threads窗口中看到的操作系统线程ID.这有点糟糕,您可能应该向您的程序添加一些转储ManagedThreadId值的日志记录,以便您可以将值与线程匹配.更新:在以后的VS版本中修复,Debug> Windows> Threads调试器窗口现在显示ManagedThreadId.

  • `&locker`似乎不起作用.只是给出"无法评估表达式".`locker`有效,但那不会让我看到你描述的线程ID.(VS2010) (8认同)

SwD*_*n81 5

是的,您可以在 VS 中使用“线程”视图。在您的应用程序中的任何地方中断(或单击“全部中断”按钮),然后您可以选择每个线程并查看谁拥有锁定(如果有的话)。

要添加它,请转到调试 > Windows > 线程 (Ctrl+D,T)

  • 谢谢。我知道这个技术。但是如果线程数非常多,则非常耗时 (4认同)

ste*_*ook 5

您可以实现一个Monitor包装器,在输入时保存堆栈跟踪和线程名称.

旧方式:

private object myLock = new object();

...
lock(myLock)
{
    DoSomething();
}
...
Run Code Online (Sandbox Code Playgroud)

使用以下代码:

private SmartLock myLock = new SmartLock();

...
myLock.Lock( () =>
{
    DoSomething();
}
);
...
Run Code Online (Sandbox Code Playgroud)

资源:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Cla*_*len 5

最近,我试图确定持有锁定的功能,发现以下内容非常有用,并且以前在任何地方都没有看到过。我在这里将其作为答案,以防其他人也发现它有用。

早先发布的许多其他解决方案都需要编写一个新类,然后将所有lock(blah)转换为BetterLock(blah),这需要大量的调试工作,并且您可能不希望在代码的生产/发布版本中使用它。其他人则需要附加调试器,该调试器会更改代码的时序并可能掩盖问题。

相反,请尝试以下操作...

原始代码:

object obj = new object();
lock(obj)
{
    // Do stuff
}
Run Code Online (Sandbox Code Playgroud)

修改后的调试代码:

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
}
Run Code Online (Sandbox Code Playgroud)

通过将obj作为属性,至少暂时,有非常最少的代码更改就可以决定哪些功能获取的锁最后也是什么的线程-在跟踪输出中的最后一项刚刚的样子。当然,您也可以输出在getter中可能有用的任何其他信息。

不,这不会让你确定锁被释放的时候,但如果它及时得到释放,那么你实际上没有一个锁争用问题摆在首位。

  • 跟踪应该声明“尝试获取锁”。在高争用情况下,跟踪中的消息顺序可能与线程获取锁的实际顺序不同。 (2认同)