Таинственное взаимоблокировка с ReaderWriterLockSlim - PullRequest
3 голосов
/ 02 апреля 2019

Я написал довольно банальную обертку вокруг ReaderWriterLockSlim:

class SimpleReaderWriterLock
{
    private class Guard : IDisposable
    {
        public Guard(Action action)
        {
            _Action = action;
        }

        public void Dispose()
        {
            _Action?.Invoke();
            _Action = null;
        }

        private Action _Action;
    }

    private readonly ReaderWriterLockSlim _Lock
        = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

    public IDisposable ReadLocked()
    {
        _Lock.EnterReadLock();
        return new Guard(_Lock.ExitReadLock);
    }

    public IDisposable WriteLocked()
    {
        _Lock.EnterWriteLock();
        return new Guard(_Lock.ExitWriteLock);
    }

    public IDisposable UpgradableReadLocked()
    {
        _Lock.EnterUpgradeableReadLock();
        return new Guard(_Lock.ExitUpgradeableReadLock);
    }
}

(Вероятно, это не самая эффективная вещь в мире, поэтому меня также интересуют предлагаемые улучшения для этого класса.)

Используется так:

using (_Lock.ReadLocked())
{
    // protected code
}

(Значительное число операций чтения происходит очень часто, и почти никогда не происходит никаких записей.)

Кажется, что это всегда работает должным образом в режиме выпуска и в производстве. Однако в режиме отладки и в отладчике очень редко процесс блокируется в особом состоянии - он вызвал EnterReadLock, сама блокировка не удерживается ничем (владельцем является 0, свойства, которые сообщают, есть ли у него читатели) / писатели / официанты не говорят и т. д.) , но блокировка вращения внутри заблокирована, и она там бесконечно вращается.

Я не знаю, что вызывает это, за исключением того, что, кажется, это происходит чаще, если я останавливаюсь на точках останова и пошагово (в совершенно не связанном коде).

Если я вручную переключу поле спинблока _isLocked обратно на 0, то процесс возобновится, и после этого все будет работать так, как ожидалось.

Что-то не так с кодом или с самой блокировкой? Делает ли отладчик что-то, чтобы случайно вызвать взаимоблокировку спин-блокировки? (Я использую .NET 4.6.2.)

Я прочитал статью , которая указывает, что ThreadAbortException может быть проблемой для этих блокировок - и мой код действительно имеет вызовы Abort() в некоторых местах - но я не думаю те, которые включают в себя код, который вызывает этот заблокированный код (хотя я могу ошибаться), и если проблема была в том, что блокировка была получена и никогда не снималась, то это должно выглядеть не так, как я вижу. (Несмотря на это, в документации по фреймворку специально запрещается получение блокировки в ограниченном регионе, как рекомендуется в этой статье.)

Я могу изменить код, чтобы избежать перенаправления блокировки, но разве using не защищает рекомендуемую практику в целом?

1 Ответ

0 голосов
/ 02 апреля 2019

Поскольку оператор using не является безопасным для прерывания , вы можете попробовать заменить его на обходное решение для безопасного прерывания, предложенное в связанной статье . Как то так:

public void WithReadLock(Action action)
{
    var lockAcquired = false;
    try
    {
        try { }
        finally
        {
            _Lock.EnterReadLock();
            lockAcquired = true;
        }
        action();
    }
    finally
    {
        if (lockAcquired) _Lock.ExitReadLock();
    }
}

Использование:

var locker = new SimpleReaderWriterLock();
locker.WithReadLock(() =>
{
    // protected code
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...