Как синхронизировать доступ к глобальной переменной с очень частыми операциями чтения / записи? - PullRequest
9 голосов
/ 30 сентября 2011

Я работаю над инфраструктурой ведения журнала отладки для серверного приложения. Каждая точка регистрации в исходном коде указывает свой уровень (CRITICAL, ERROR и т. Д.) Среди других параметров. Таким образом, точка входа в исходный код выглядит так:

DBG_LOG_HIGH( … )

, который является макросом, который расширяется до

if ( CURRENT_DEBUG_LOG_LEVEL >= DEBUG_LOG_LEVEL_HIGH ) {
   // prepare and emit log record
}

, где DEBUG_LOG_LEVEL_HIGH - это предопределенная константа (скажем, 2), а CURRENT_DEBUG_LOG_LEVEL - это некоторое выражение, которое оценивает текущий уровень ведения журнала отладки, установленный пользователем. Простейшим подходом было бы определить CURRENT_DEBUG_LOG_LEVEL как:

extern int g_current_debug_log_level;
#define CURRENT_DEBUG_LOG_LEVEL (g_current_debug_log_level)

Я хотел бы разрешить пользователю изменять текущий уровень ведения журнала отладки во время выполнения приложения, и это нормально, чтобы изменение вступило в силу через несколько секунд. Приложение является многопоточным, и изменения в g_current_debug_log_level можно легко сериализовать (например, CRITICAL_SECTION), но чтобы не влиять на производительность, выражение ( CURRENT_DEBUG_LOG_LEVEL >= DEBUG_LOG_LEVEL_HIGH ) должно выполняться как можно быстрее, поэтому я бы хотел избежать использования какого-либо потока Механизм синхронизации есть.

Итак, мои вопросы:

  1. Может ли отсутствие синхронизации в чтениях g_current_debug_log_level вызвать считывание неверного значения? Хотя это не должно влиять на корректность приложения, поскольку пользователь мог в любом случае установить текущий уровень ведения журнала отладки на неверное значение, это может повлиять на производительность приложения, поскольку это может привести к тому, что он будет генерировать очень большой объем журнала отладки в течение неуправляемого периода времени.

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

Буду также признателен за любые предложения по альтернативным реализациям, которые удовлетворяют вышеуказанным требованиям (минимальное влияние на производительность для сравнения уровней и синхронное изменение уровня с задержкой не более нескольких секунд).

Ответы [ 6 ]

4 голосов
/ 30 сентября 2011

Нет ничего, что требовало бы, чтобы запись, сделанная в одном потоке на одном ядре, когда-либо становилась видимой для другого потока, читающего на другом ядре, без какого-либо ограничения для создания края «произойдет до» между записью и чтением .

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

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

Но использование правильного ограждения для обеспечения того, что происходит до того, как край действительно является правильным ответом. FWIW, C ++ 11 предоставляет явную модель памяти, которая определяет семантику и предоставляет такие виды операций ограждения на уровне языка. Но, насколько я знаю, ни один компилятор еще не реализует новую модель памяти. Поэтому для C / C ++ вам нужно использовать блокировку из библиотеки или явное ограждение.

1 голос
/ 30 сентября 2011

Если вы работаете в Windows, а Windows работает только на x86 (что в настоящее время в основном верно, но может измениться ...), и, если только один поток когда-либо записывает в переменную, вы можете уйти без какой-либо синхронизациичто бы то ни было.

Чтобы быть "правильным", вы должны использовать блокировку чтения-записи какой-либо формы.

0 голосов
/ 20 мая 2012

Определите порядковую переменную, видимую в области действия, и обновите ее соответствующим образом (при изменении уровня журнала). Если данные правильно выровнены (т.е. по умолчанию), то вам не нужно ничего особенного, кроме объявления вашей текущей переменной журнала a ".летучий».Это будет работать для длинного размера (32-битный порядковый номер).Таким образом, ваше решение будет:

extern volatile long g_globalLogLevel;

Нет необходимости во внешней синхронизации (например, RWlock / CriticalSection / Spin и т. Д.)

0 голосов
/ 10 мая 2012

На x86 и x64 volatile будет стоить очень мало прямых затрат.Могут быть некоторые косвенные затраты, связанные с принудительным повторным извлечением несвязанных переменных (доступ к изменчивым переменным рассматривается как ограждения памяти уровня компилятора для всех других переменных «адрес взят»).Представьте переменную volatile как вызов функции, так как компилятор будет терять информацию о состоянии памяти при вызове.

В Itanium volatile имеет определенную стоимость, но не так уж и плохо.В ARM компилятор MSVC по умолчанию не предоставляет барьеры (и не обеспечивает упорядочение) для volatile.

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

0 голосов
/ 01 октября 2011

Посмотрите на новые блокировки Slim Reader / Writer, доступные в Vista и 7. Они должны делать то, что вы хотите, с минимальными накладными расходами:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937(v=vs.85).aspx

0 голосов
/ 30 сентября 2011

Учитывая вашу текущую реализацию, я предлагаю вам взглянуть на атомарные операции. Если это предназначено только для Windows, посмотрите Доступ к блокируемой переменной

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