Синхронизация потоков 101

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

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

На работе я пытался отладить некоторый многопоточный код, и я столкнулся с этим:

EnterCriticalSection(&m_Crit4);
m_bSomeVariable = true;
LeaveCriticalSection(&m_Crit4);

Сейчас,m_bSomeVariable это Win32 BOOL (не энергозависимый), который, насколько я знаю, определен как int, а при чтении и записи x86 эти значения являются одной инструкцией, и поскольку переключение контекста происходит на границе инструкций, нет необходимости в синхронизации эта операция с критическим разделом.

Я провел еще несколько исследований в Интернете, чтобы выяснить, не нуждается ли эта операция в синхронизации, и предложил два сценария:

ЦП реализует не по порядку исполнения, или второй поток работает на другом ядре, и обновленное значение не записывается в ОЗУ, чтобы другое ядро ​​могло видеть; а такжеInt не выровнен по 4 байта.

Я считаю, что номер 1 можно решить с помощью ключевого слова «volatile». В VS2005 и более поздних версиях компилятор C ++ окружает доступ к этой переменной с помощью барьеров памяти, гарантируя, что переменная всегда полностью записывается / читается в основную системную память перед ее использованием.

Номер 2 Я не могу проверить, я не знаю, почему выравнивание байтов будет иметь значение. Я не знаю набор инструкций x86, ноmov нужно дать 4-байтовый выровненный адрес? Если нет, вам нужно использовать комбинацию инструкций? Это привело бы к проблеме.

Так...

ВОПРОС 1: Освобождает ли программист от необходимости синхронизировать 4-байтовый / 8-байтовый переменный x86 / x64 между операциями чтения / записи, используя ключевое слово «volatile» (простота с использованием барьеров памяти и подсказку компилятору не оптимизировать этот код)?

ВОПРОС 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: Где наиболее подходят критические секции / мьютексы / семафоры / спин-блокировки? То есть к какой проблеме синхронизации они должны быть применены. Существует ли огромный ущерб производительности при выборе одного над другим?

И пока мы на нем, я читал, что спин-блокировки не следует использовать в одноядерной многопоточной среде, а только в многоядерной многопоточной среде. Так,ВОПРОС 5: Это неправильно, а если нет, то почему?

Заранее спасибо за любые ответы :)

Ответы на вопрос(6)

Ваш ответ на вопрос