Раньше я написал очень простой многопоточный код, и я всегда знал, что в любой момент может быть контекстный переключатель прямо в середине того, что я делаю, поэтому я всегда защищал доступ к разделяемые переменные через класс CCriticalSection, который входит в критический раздел построения и оставляет его при уничтожении. Я знаю, что это довольно агрессивно, и я вхожу и оставляю критические разделы довольно часто, а иногда и вопиюще (например, в начале функции, когда я мог бы наложить CCriticalSection внутри более жесткого блока кода), но мой код не сбой и он работает достаточно быстро.
На работе мой многопоточный код должен быть более жестким, только блокировка/синхронизация на самом низком уровне.
На работе я пытался отлаживать многопоточный код, и я наткнулся на это:
EnterCriticalSection(&m_Crit4);
m_bSomeVariable = true;
LeaveCriticalSection(&m_Crit4);
Теперь m_bSomeVariable
является Win32 BOOL (не изменчивым), который, насколько мне известно, определяется как int, а при чтении и записи x86 эти значения представляют собой одну инструкцию, и поскольку контекстные переключатели происходят на тогда нет необходимости в синхронизации этой операции с критическим сектором.
Я провел еще несколько исследований в Интернете, чтобы убедиться, что эта операция не требует синхронизации, и я придумал два сценария:
- ЦП реализует не исполнение заказа или второй поток работает на другом ядре, а обновленное значение не записывается в ОЗУ для другого ядра; и
- int не выравнивается по 4 байт.
Я считаю, что число 1 можно решить, используя ключевое слово "volatile". В VS2005 и более поздних версиях компилятор С++ обеспечивает доступ к этой переменной с использованием барьеров памяти, гарантируя, что переменная всегда полностью записывается/считывается в основную системную память перед ее использованием.
Номер 2 Я не могу проверить, я не знаю, почему выравнивание байтов изменило бы ситуацию. Я не знаю набор инструкций x86, но нужно ли mov
задать 4-байтовый выровненный адрес? Если вам не нужно использовать комбинацию инструкций? Это создаст проблему.
Итак...
ВОПРОС 1: Использует ли ключевое слово "volatile" (неявное использование барьеров памяти и намекает на компилятор, чтобы не оптимизировать этот код) освобождает программиста от необходимости синхронизировать 4-байтовый/8 -byte на переменную x86/x64 между операциями чтения/записи?
ВОПРОС 2: Есть ли явное требование, чтобы переменная была выровнена по 4 байта /8 байтов?
Я сделал еще несколько копаний в наш код и переменные, определенные в классе:
class CExample
{
private:
CRITICAL_SECTION m_Crit1; // Protects variable a
CRITICAL_SECTION m_Crit2; // Protects variable b
CRITICAL_SECTION m_Crit3; // Protects variable c
CRITICAL_SECTION m_Crit4; // Protects variable d
// ...
};
Теперь для меня это кажется чрезмерным. Я думал, что критические разделы синхронизировали потоки между процессом, поэтому, если у вас есть один, вы можете ввести его, и ни один другой поток в этом процессе не сможет выполнить. Нет необходимости в критическом разделе для каждой переменной, которую вы хотите защитить, если вы находитесь в критическом разделе, тогда ничто не может вас прервать.
Я думаю, что единственное, что может изменить переменные из-за пределов критического раздела, - это то, что процесс разделяет страницу памяти с другим процессом (можете ли вы это сделать?), а другой процесс начинает изменять значения. Мьютексы также помогут здесь, названные мьютексы разделяются между процессами или только процессы с тем же именем?
ВОПРОС 3: Является ли мой анализ критических секций правильным, и должен ли этот код быть переписан для использования мьютексов? Я посмотрел на другие объекты синхронизации (семафоры и спин-блоки), они лучше подходят здесь?
ВОПРОС 4: Где наиболее подходящие разделы/мьютексы/семафоры/спин-блоки? То есть, к какой проблеме синхронизации они должны применяться. Существует ли значительное ограничение производительности при выборе одного из них?
И пока мы на нем, я читал, что spinlocks не следует использовать в одноядерной многопоточной среде, а только в многоядерной многопоточной среде. Итак, ВОПРОС 5: Является ли это неправильным, а если нет, то почему это правильно?
Заранее благодарим за любые ответы:)