4

我正在编写一个广泛使用多线程的应用程序。一些线程使用 ReaderWriterLockSlim 共享一个 observablecollection。

我不时遇到死锁,我需要知道在死锁时哪个线程持有锁。我怎么知道这个?我查看了对象属性,没有明显的东西。目前我只知道哪些线程正在等待锁。

谢谢你的帮助!

编辑:当然,我说的是在调试时找到它,并提供所有可用的调试信息。

4

5 回答 5

3

在死锁期间,只需查看线程调试面板中的当前线程,查看调用堆栈,您就会发现哪个线程获得了锁。

如果您需要知道代码中的线程 id,您可以随时将其静态保存,或者从 readerwriterlockslim 继承并添加线程属性。

于 2012-05-23T20:30:00.317 回答
3

这就是我的意思。
只需跟踪锁定和解锁,当您遇到死锁时,系统将停止,最后一个“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();
        }
    }  
}
于 2012-05-24T19:50:42.040 回答
2

您总是可以尝试在锁定之前和之后跟踪线程 ID,这样您就可以记录发生了什么以及谁锁定了它以及何时锁定。您可以写入文件或仅检查调试器输出窗口以查看所有跟踪。我相信您可以使用跟踪断点(Breakpoint -> When Hit...)而不是真正的跟踪代码来在输出窗口中快速获取一些东西。

于 2012-05-23T20:43:29.477 回答
2

ReaderWriterLockSlim没有密封,因此您可以将其子类化并以这种方式附加您需要的任何信息。问题是有用的方法不是虚拟的,所以你不能覆盖它们。但是,除了捕获调用该方法的线程之外,您还可以添加自己的方法,例如调用EnterReadLockDebug和在幕后。这不是一个很好的解决方案,因为您必须更改所有呼叫站点。但是,如果使用调试器太麻烦,那么这可能是一个合理的选择。ExitReadLockDebugEnterReadLockExitReadLock

使用条件编译的主题有很多变化。您可以检测 Debug vs. Release 构建,并根据激活的构建配置注入必要的调试逻辑。Debug 激活时注入调试信息,Release 激活时省略。

于 2012-05-23T21:12:58.447 回答
0

这是我结束的代码,以供将来参考:

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
}
}
于 2012-05-24T12:54:23.370 回答