Поведение неблокированного доступа к синхронизированной коллекции в C# - PullRequest
1 голос
/ 10 апреля 2020

У меня есть частная коллекция (например, ISet<int>), доступ к которой можно изменить или изменить несколькими потоками.

У меня есть logi c, так что этой теме нужно только изменить ISet в определенные состояния, тогда как другой поток всегда должен мутировать его. Другой поток всегда будет принимать lock, реализовав ключевое слово lock для ISet следующим образом:

lock (this._set)
{
    // some mutating operation on this._set
}

Для этого потока мне нужно только выполнить операцию, которая изменяет поток, если ISet содержит int, называемый currentValue, что в этом случае встречается редко. Операция в другом потоке является частой. Поэтому вопрос заключается в том, могу ли я проверить, содержит ли ISet currentValue перед тем, как взять lock, чтобы минимизировать время, в течение которого этот поток блокирует другой поток, например:

if (this._set.Contains(currentValue))
{
    lock (this._set)
    {
            if (this._set.Contains(currentValue))
            {
                // some mutating operation on this._set
            }
    }
}

Вопрос в том, является ли поведение проверки вне lock неопределенным или может ли это привести к переводу ISet в неопределенное или неожиданное состояние. Конечно, фактическое возвращаемое значение нельзя доверять, поэтому его снова проверяют внутри lock, но действительно ли это допустимая оптимизация, которую я могу ожидать, или она испортит некоторое внутреннее состояние набора? Цель состоит в том, чтобы использовать lock в этой теме как можно реже.

1 Ответ

0 голосов
/ 21 апреля 2020

Простое чтение значений коллекции не вызовет исключения и не изменит значения коллекции, если вы это имеете в виду. Он может вернуть грязные данные, но вы уже заявили, что ожидаете такой возможности. Я полагаю, что чтение из не поточно-безопасной коллекции, как эта, может даже дать вам неполные данные или данные, которые имеют половину старого состояния, половину нового состояния (см. Этот ответ на C# многопоточность: необходима ли блокировка чтения?

Предположим, что первый тест для _set.Contains(currentValue) возвращает true, а другой поток обновляет коллекцию сразу после удаления currentValue. Затем вы вводите блокировку, но теперь, когда вы тестируете _set.Contains(currentValue) , он возвращает false.

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

Было бы трудно предоставить вам хорошую альтернативу, не зная других требований, но я думаю, что я ответил на ваши вопросы и показал, что код действительно не оптимизирован вообще, но само по себе ничего не сломает.

...