Использование volatile (Thread.VolatileRead / Thread.VolatileWrite) в C # - PullRequest
8 голосов
/ 08 декабря 2008

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

Другими словами, могут ли объекты кучи кэшироваться на процессоре?

Используя приведенный ниже пример и предполагая, что многопоточность будет обращаться к методам GetValue и Add, мне нужно, чтобы ThreadA мог добавить данные (используя метод Add), а ThreadB мог сразу же увидеть / получить эти добавленные данные (используя метод GetValue). Так что мне нужно добавить изменчивое чтение / запись в _data, чтобы обеспечить это? По сути, я не хочу добавлять данные для кэширования на процессоре ThreadA.

/ Я не блокирую (навязываю монопольный доступ к потокам), так как код должен быть очень быстрым, и я не удаляю никакие данные из _data, поэтому мне не нужно блокировать _data.

Спасибо.

**** Обновление ****************************

Очевидно, что вы, ребята, думаете, что использование блокировки без использования этого примера - плохая идея. Но с какими побочными эффектами или исключениями я могу столкнуться здесь?

Может ли тип Dictionary генерировать исключение, если один поток повторяет значения для чтения, а другой поток повторяет значения для обновления? Или я бы просто испытал «грязное чтение» (что было бы хорошо в моем случае)?

**** Завершить обновление ****************************

public sealed class Data
{
    private volatile readonly Dictionary<string, double> _data = new Dictionary<string, double>();

    public double GetVaule(string key)
    {
        double value;
        if (!_data.TryGetValue(key, out value))
        {
            throw new ArgumentException(string.Format("Key {0} does not exist.", key));
        }
        return value;
    }

    public void Add(string key, double value)
    {
        _data.Add(key, value);
    }

    public void Clear()
    {
        _data.Clear();
    }
}

Спасибо за ответы. Что касается блокировок, методы почти постоянно вызываются несколькими потоками, поэтому моя проблема в оспариваемых блокировках, а не в действительной операции блокировки.

Итак, мой вопрос о кэшировании процессора, могут ли объекты кучи (поле экземпляра _data) кэшироваться на процессоре? Нужен ли мне доступ к полю _data с использованием volatile чтения / записи?

/ Также я застрял с .Net 2.0.

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

Ответы [ 6 ]

16 голосов
/ 08 декабря 2008

Документы MSDN для Dictionary<TKey, TValue> говорят, что это безопасно для нескольких читателей , но они не дают гарантию "один писатель, несколько читателей", как это делают некоторые другие классы. Короче, я бы так не поступил.

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

6 голосов
/ 01 июня 2009

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

Однако вы никогда не измените значение вашей переменной (в данном случае, это ссылка). Все, что вы делаете, это манипулируете областью памяти, на которую указывает ссылка. Объявление его как volatile readonly (которое, если мое понимание здраво, побеждает цель изменчивости, никогда не позволяя установить его) не повлияет на реальные данные, которыми манипулируют (внутреннее хранилище для Dictionary<>).

С учетом всего сказанного, вам действительно нужно использовать блокировку в этом случае. Ваша опасность простирается за пределы перспективы «грязного чтения» (то есть то, что вы читали, было бы в какой-то момент действительно) на действительно неизвестную территорию. Как сказал Джон, вам действительно нужны доказательства того, что блокировка создает неприемлемую производительность, прежде чем пытаться пойти по пути кодирования без блокировки. В противном случае это является воплощением преждевременной оптимизации.

3 голосов
/ 08 декабря 2008

Проблема в том, что ваш метод добавления:

public void Add(string key, double value)
{
    _data.Add(key, value);
}

Может привести к тому, что _data решит полностью реорганизовать данные, которые в нем хранятся - в этот момент запрос GetVaule может завершиться неудачей любым возможным способом.

Вам нужна блокировка или другая реализация структуры данных / структуры данных.

2 голосов
/ 08 декабря 2008

Я не думаю, что volatile может заменить блокировку, если вы начнете вызывать методы для нее. Вы гарантируете, что поток A и поток B видят одну и ту же копию словаря, но вы все равно можете получить доступ к словарю одновременно. Вы можете использовать многомодовые блокировки для повышения параллелизма. См. Например, ReaderWriterLockSlim.

Представляет блокировку, которая используется для управлять доступом к ресурсу, позволяя несколько потоков для чтения или эксклюзивный доступ для письма.

1 голос
/ 25 сентября 2010

Ключевое слово volatile не относится к блокировке, оно используется для указания того, что значение указанного поля может быть изменено или прочитано другим потоком или другим способом, который может работать одновременно с вашим кодом. Это важно знать компилятору, поскольку многие процессы оптимизации включают в себя кэширование значения переменной и перестановку инструкций. Ключевое слово volatile скажет компилятору быть осторожным при оптимизации тех инструкций, которые ссылаются на переменную volatile.

Для многопоточного использования словаря есть много способов сделать. Самый простой способ - использовать ключевое слово lock, которое имеет достаточную производительность. Если вам нужна более высокая производительность, вам может потребоваться реализовать собственный словарь для конкретной задачи.

0 голосов
/ 18 февраля 2010

Volatile не блокирует, он не имеет ничего общего с синхронизацией. Как правило, безопасно выполнять чтение без блокировки данных только для чтения. Обратите внимание, что только потому, что вы ничего не удаляете из _data, вы, похоже, вызываете _data.Add (). Это НЕ только для чтения. Так что да, этот код взорвется вам в лицо различными захватывающими и трудно предсказуемыми способами.

Используйте замки, это просто, это безопаснее. Если вы гуру без блокировок (а вы нет!), И профилирование показывает узкое место, связанное с конфликтом за блокировку, И вы не можете решить проблемы конкуренции посредством разбиения на разделы или переключения на спин-блокировки. исследовать решение для получения чтения без блокировки, которое потребует написания собственного словаря с нуля и МОЖЕТ быть быстрее, чем решение для блокировки.

Вы начинаете видеть, как далеко вы находитесь от базы в своем мышлении здесь? Просто используйте чертов замок!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...