我正在编写一个广泛使用多线程的应用程序。一些线程使用 ReaderWriterLockSlim 共享一个 observablecollection。
我不时遇到死锁,我需要知道在死锁时哪个线程持有锁。我怎么知道这个?我查看了对象属性,没有明显的东西。目前我只知道哪些线程正在等待锁。
谢谢你的帮助!
编辑:当然,我说的是在调试时找到它,并提供所有可用的调试信息。
我正在编写一个广泛使用多线程的应用程序。一些线程使用 ReaderWriterLockSlim 共享一个 observablecollection。
我不时遇到死锁,我需要知道在死锁时哪个线程持有锁。我怎么知道这个?我查看了对象属性,没有明显的东西。目前我只知道哪些线程正在等待锁。
谢谢你的帮助!
编辑:当然,我说的是在调试时找到它,并提供所有可用的调试信息。
在死锁期间,只需查看线程调试面板中的当前线程,查看调用堆栈,您就会发现哪个线程获得了锁。
如果您需要知道代码中的线程 id,您可以随时将其静态保存,或者从 readerwriterlockslim 继承并添加线程属性。
这就是我的意思。
只需跟踪锁定和解锁,当您遇到死锁时,系统将停止,最后一个“Enter”将指向锁定线程的方向。
public class ReaderWriterLockSlimExtended : ReaderWriterLockSlim
{
private Thread m_currentOwnerThread = null;
private object m_syncRoot = new object();
public Thread CurrentOwnerThread
{
get
{
lock (m_syncRoot)
{
return m_currentOwnerThread;
}
}
}
public Thread CurrentOwnerThreadUnsafe
{
get
{
return m_currentOwnerThread;
}
}
public new void EnterWriteLock()
{
lock (m_syncRoot)
{
base.EnterWriteLock();
m_currentOwnerThread = Thread.CurrentThread;
}
Debug.WriteLine("Enter Write Lock - Current Thread : {0} ({1})", CurrentOwnerThread.Name, CurrentOwnerThread.ManagedThreadId);
}
public new void ExitWriteLock()
{
Debug.WriteLine("Exit Write Lock - Current Thread : {0} ({1})", CurrentOwnerThread.Name, CurrentOwnerThread.ManagedThreadId);
lock (m_syncRoot)
{
m_currentOwnerThread = null; //Must be null before exit!
base.ExitWriteLock();
}
}
}
您总是可以尝试在锁定之前和之后跟踪线程 ID,这样您就可以记录发生了什么以及谁锁定了它以及何时锁定。您可以写入文件或仅检查调试器输出窗口以查看所有跟踪。我相信您可以使用跟踪断点(Breakpoint -> When Hit...)而不是真正的跟踪代码来在输出窗口中快速获取一些东西。
ReaderWriterLockSlim
没有密封,因此您可以将其子类化并以这种方式附加您需要的任何信息。问题是有用的方法不是虚拟的,所以你不能覆盖它们。但是,除了捕获调用该方法的线程之外,您还可以添加自己的方法,例如调用EnterReadLockDebug
和在幕后。这不是一个很好的解决方案,因为您必须更改所有呼叫站点。但是,如果使用调试器太麻烦,那么这可能是一个合理的选择。ExitReadLockDebug
EnterReadLock
ExitReadLock
使用条件编译的主题有很多变化。您可以检测 Debug vs. Release 构建,并根据激活的构建配置注入必要的调试逻辑。Debug 激活时注入调试信息,Release 激活时省略。
这是我结束的代码,以供将来参考:
using System;
using System.Threading;
namespace Utils
{
public class ReaderWriterLockSlim2
{
#region Attributes
private readonly TimeSpan _maxWait;
private readonly ReaderWriterLockSlim _lock;
#endregion
#region Properties
public int CurrentWriteOwnerId { get; private set; }
public string CurrentWriteOwnerName { get; private set; }
#endregion
#region Public Methods
public ReaderWriterLockSlim2(LockRecursionPolicy policy, TimeSpan maxWait)
{
_maxWait = maxWait;
_lock = new ReaderWriterLockSlim(policy);
}
public void EnterWriteLock()
{
if (!_lock.TryEnterWriteLock(_maxWait))
{
throw new TimeoutException(string.Format("Timeout while waiting to enter a WriteLock. Lock adquired by Id {0} - Name {1}", this.CurrentWriteOwnerId, this.CurrentWriteOwnerName));
}
else
{
this.CurrentWriteOwnerId = Thread.CurrentThread.ManagedThreadId;
this.CurrentWriteOwnerName = Thread.CurrentThread.Name;
}
}
public void ExitWriteLock()
{
_lock.ExitWriteLock();
this.CurrentWriteOwnerId = 0;
this.CurrentWriteOwnerName = null;
}
public void EnterReadLock()
{
_lock.EnterReadLock();
}
public void ExitReadLock()
{
_lock.ExitReadLock();
}
#endregion
}
}