Как узнать, кому принадлежит ReaderWriterLockSlim?

Я пишу приложение, которое широко использует многопоточность. Некоторые потоки совместно используют наблюдаемую коллекцию с помощью ReaderWriterLockSlim.

Время от времени у меня возникает тупик, и мне нужно знать, какой поток удерживает блокировку в момент тупика. Откуда я могу это знать? Я просмотрел свойства объекта, и там не было ничего очевидного. В настоящее время все, что я знаю, это то, какие потоки ожидают блокировки.

Спасибо за вашу помощь!

РЕДАКТИРОВАТЬ: Конечно, я говорю о том, чтобы найти его во время отладки со всей доступной отладочной информацией.


person Ignacio Soler Garcia    schedule 23.05.2012    source источник


Ответы (5)


Во время взаимоблокировки просто посмотрите на текущие потоки в панели отладки потоков, просмотрите свой стек вызовов, и вы узнаете, какой поток взял блокировку.

Если вам нужно знать идентификатор потока в своем коде, вы всегда можете сохранить его статически или унаследовать от readerwriterlockslim и добавить свойство потока.

person Erez Robinson    schedule 23.05.2012
comment
У меня есть от 20 до 30 потоков. Тот, кто взял поток, вероятно, сделал это в далеком методе, поэтому просмотра стеков вызовов недостаточно, я надеялся, что у readerwriterlock уже есть свойство, указывающее текущего владельца. - person Ignacio Soler Garcia; 24.05.2012
comment
Если у вас 20 или 30 потоков, то обход каждого потока может быть утомительным. Однако, если вы говорите, что зашли в тупик, то, скорее всего, вы увидите, что все потоки ожидают в одной и той же позиции, а один поток ожидает вызова или чего-то подобного. просто быстро посмотрите на столбец местоположения на панели потоков. - person Erez Robinson; 24.05.2012
comment
Создайте потомок, но после того, как вы разрешите тупик, замените его исходным тонким шкафчиком. - person Erez Robinson; 24.05.2012
comment
К сожалению, они делают довольно разные вещи, и у меня обычно есть 2 ожидающих потока и еще один, который заблокировал эти ... в середине еще 15 потоков ... :S В любом случае с потомком я могу сделать эту работу. Спасибо. - person Ignacio Soler Garcia; 25.05.2012

Вы всегда можете попытаться отследить идентификатор потока непосредственно до и после блокировки, чтобы вы записывали, что произошло, кто заблокировал и когда. Вы можете записать в файл или просто проверить в окне вывода отладчика, чтобы увидеть все трассировки. Я считаю, что вы могли бы использовать точку останова трассировки (точка останова -> при попадании...) вместо реального кода трассировки, чтобы быстро получить что-то в окне вывода.

person Slobodan Savkovic    schedule 23.05.2012

ReaderWriterLockSlim не запечатан, поэтому вы можете подклассифицировать его и таким образом прикрепить любую необходимую вам информацию. Проблема в том, что полезные методы не являются виртуальными, поэтому их нельзя переопределить. Но вы можете добавить свои собственные методы, такие как EnterReadLockDebug и ExitReadLockDebug и им подобные, которые вызывают EnterReadLock и ExitReadLock за кулисами в дополнение к захвату потока, в котором вызывается метод. Это не лучшее решение, потому что вам придется изменить все места вызова. Но если использование отладчика слишком громоздко, возможно, это будет разумной альтернативой.

Существует множество вариаций темы с использованием условной компиляции. Вы можете обнаружить сборку отладки и выпуска и внедрить необходимую логику отладки в зависимости от того, какая конфигурация сборки активна. Вводите отладочную информацию, когда активен Debug, и пропускайте ее, когда активен Release.

person Brian Gideon    schedule 23.05.2012

Вот что я имел в виду.
Просто проследите блокировки и разблокировки, и когда вы дойдете до тупика, система остановится, а последний «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();
        }
    }  
}
person Erez Robinson    schedule 24.05.2012

Это код, которым я закончил, для дальнейшего использования:

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
}
}
person Ignacio Soler Garcia    schedule 24.05.2012