我的应用程序中的一个线程在以下锁定语句中被阻塞并导致死锁
void ExecuteCommand()
{
lock(this._lockinstance)
{
// do some operation
}
}
是否可以轻松识别当前持有锁的线程?..我的应用程序有超过50个线程,这使得使用visual studio难以通过每个callstack来定位持有锁的线程
答案 0 :(得分:17)
尝试一些示例代码:
class Test {
private object locker = new object();
public void Run() {
lock (locker) { // <== breakpoint here
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
}
}
}
在指示的行上设置断点。当它中断时,使用Debug + Windows + Memory + Memory 1.右键单击窗口并选择“4-byte Integer”。在“地址”框中,键入&amp; locker。第二个字是拥有锁的线程的线程ID。跳过锁定语句以查看其更改。
请注意,该号码是托管线程ID,而不是您在Debug + Windows + Threads窗口中看到的操作系统线程ID。这有点糟糕,您可能应该向您的程序添加一些转储ManagedThreadId值的日志记录,以便您可以将值与线程匹配。更新:修复了以后的VS版本,Debug&gt; Windows&gt;线程调试器窗口现在显示ManagedThreadId。
答案 1 :(得分:5)
您可以实现一个可以节省堆栈跟踪和监控器的Monitor包装器。输入时的线程名称。
旧方式:
private object myLock = new object();
...
lock(myLock)
{
DoSomething();
}
...
使用以下代码:
private SmartLock myLock = new SmartLock();
...
myLock.Lock( () =>
{
DoSomething();
}
);
...
来源:
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);
}
}
}
答案 2 :(得分:4)
是的,你可以在VS中使用'Threads'视图。在应用程序的任何地方中断(或单击“全部中断”按钮),然后您可以选择每个线程并查看谁拥有锁定(如果有人)。
要添加它,请转到Debug&gt; Windows&gt;线程(Ctrl + D,T)
答案 3 :(得分:3)
旧帖子已经过时了。
但是我想我可能会给出一个解决方案,我发现它对于尝试追踪死锁和其他锁定问题非常有用。
我为我的锁使用一次性类 - 我喜欢Monitor
但可以使用任何锁定机制。
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);
}
}
我使用带有名字的锁定对象,这样我就可以清楚地知道我想要获取哪个锁。
public class LockObject
{
public string Name { get; set; }
public LockObject(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}
最后创建一个锁对象,然后在一个使用块中保存该对象。
//create an object to lock on
private readonly object _requestLock = new LockObject("_requestLock");
using (MonitorLock.CreateLock(_requestLock))
{
//do some work
}
输出应该是
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
希望有人发现这有用:)
答案 4 :(得分:3)
最近我试图确定哪个函数持有一个锁,发现以下内容非常有用,之前在任何地方都没有看过。我已将此作为答案放在此处,以防其他人发现它也有用。
之前发布的许多其他解决方案都需要编写一个新类,然后将所有锁(blah)转换为BetterLock(blah),这对于调试很有用,而且你可能不想在生产/发布的版本中你的代码。其他人需要附加调试器,这会改变代码的时间并且可能会使问题变得模糊。
相反,请尝试以下方法......
object obj = new object();
lock(obj)
{
// Do stuff
}
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
}
通过将obj
作为属性公开,至少是暂时的,通过非常最小的代码更改,您可以确定最后获取锁的函数以及在哪个线程上获取 - 只需查看跟踪输出为最后一个条目。当然,您也可以输出您在吸气器中可能会发现的任何其他信息。
不,这不会让你确定何时发布锁定,但如果 及时发布,那么你实际上并没有锁定争用问题第一名。
答案 5 :(得分:1)
来自http://mse.codeplex.com/或http://www.microsoft.com/downloadS/details.aspx?FamilyID=80cf81f7-d710-47e3-8b95-5a6555a230c2&displaylang=en的托管堆栈浏览器在这种情况下非常出色。
它挂钩运行托管代码(需要适当的权限),包括实时代码,并抓取正在运行的线程列表。您可以双击其中任何一个或(在这种情况下更有用)选择批次并按Enter键以快速相对非侵入性(显然它将消耗资源,但它会尽可能快地进出)转储当前不同线程的堆栈。非常适合找到死锁,无限循环,近无限循环(对于那些当你的应用意外地依赖于天文学家对地球将持续多久有希望完成的悲观情绪时)以及其他此类情况。