سؤال

I'm writting an application that does extensive use of multithreading. Some of the threads share an observablecollection using a ReaderWriterLockSlim.

I'm having from time to time a deadlock and I need to know which is the thread holding the lock at the moment of the deadlock. How can I know this? I've looked at the object properties and nothing obvious was there. Currently all I know is which threads are waiting for the lock.

Thanks for your help!

EDIT: Of course I'm talking about finding it at debug time with all the debug information available.

هل كانت مفيدة؟

المحلول

During your deadlock just look at the current threads in the thread debugging panel, go through your call stack and you'll find out which thread took the lock.

If you need to know the thread id in your code, you can always save it staticly, or inherit from readerwriterlockslim and add a thread property.

نصائح أخرى

You can always try tracing the thread ID just before and after the lock, so you have written record of what happened and who locked it and when. You can write to a file or just check in the debugger output window to see all the traces. I believe you could use trace breakpoint (Breakpoint -> When Hit...) instead of real tracing code to have quick something in the output window.

ReaderWriterLockSlim is not sealed so you could subclass it and attach whatever information you need that way. The problem is that the useful methods are not virtual so you cannot override them. But, you could add your own methods like EnterReadLockDebug and ExitReadLockDebug and the like which calls EnterReadLock and ExitReadLock behind the scenes in addition to capturing the thread in which the method is called. This is not a great solution because you would have to change all of the call sites. But, if using the debugger is too cumbersome then maybe this would be a reasonable alternative.

There are many variations to theme using conditional compilation. You could detect a Debug vs. Release build and inject the necessary debugging logic depending on which build configuration is active. Inject the debugging information when Debug is active and omit it when Release is active.

Here is what I meant.
Just trace the locks and unlocks and when you get to your deadlock the system will halt and the last "Enter" will point you in the direction of the locking thread.

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();
        }
    }  
}

This is the code I ended with, for future reference:

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
}
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top