Уже отличные объяснения здесь. Тем не менее, мы можем погрузиться немного глубже. Чтобы понять разницу между основными понятиями atomic и критического сечения в OpenMP, нам нужно сначала понять концепцию lock . Давайте рассмотрим, почему нам нужно использовать замки .
Параллельная программа выполняется несколькими потоками. Детерминированные результаты произойдут тогда и только тогда, когда мы выполним синхронизацию между этими потоками. Конечно, синхронизация между потоками не всегда требуется. Мы имеем в виду те случаи, когда необходима синхронизация .
Чтобы синхронизировать потоки в многопоточной программе, мы будем использовать lock . Когда доступ должен быть ограничен только одним потоком за раз, в игру вступают lock s. Реализация концепции lock может варьироваться от процессора к процессору. Давайте выясним, как простая блокировка может работать с алгоритмической точки зрения.
1. Define a variable called lock.
2. For each thread:
2.1. Read the lock.
2.2. If lock == 0, lock = 1 and goto 3 // Try to grab the lock
Else goto 2.1 // Wait until the lock is released
3. Do something...
4. lock = 0 // Release the lock
Данный алгоритм может быть реализован на аппаратном языке следующим образом. Мы будем использовать один процессор и проанализируем поведение блокировок в этом. Для этой практики давайте предположим один из следующих процессоров: MIPS , Alpha , ARM или Power .
try: LW R1, lock
BNEZ R1, try
ADDI R1, R1, #1
SW R1, lock
Эта программа вроде бы в порядке, но это не так. Приведенный выше код страдает от предыдущей проблемы; синхронизация * * +1044. Давайте найдем проблему. Предположим, что начальное значение блокировки равно нулю. Если два потока запускают этот код, один может достигнуть SW R1, заблокировать до того, как другой прочитает переменную lock . Таким образом, они оба думают, что блокировка 1050 * свободна.
Чтобы решить эту проблему, есть другая инструкция, а не простая LW и SW . Это называется инструкция чтения-изменения-записи . Это сложная инструкция (состоящая из подинструкций), которая гарантирует, что процедура получения блокировки выполняется только одиночным потоком одновременно. Разница Read-Modify-Write по сравнению с простыми Read и Write в том, что он использует другой способ Загрузка и Сохранение . Он использует LL (Load Linked) для загрузки переменной блокировки и SC (Store Conditional) для записи в переменную блокировки. Дополнительный Link Link используется для обеспечения выполнения процедуры захвата блокировки одним потоком. Алгоритм приведен ниже.
1. Define a variable called lock.
2. For each thread:
2.1. Read the lock and put the address of lock variable inside the Link Register.
2.2. If (lock == 0) and (&lock == Link Register), lock = 1 and reset the Link Register then goto 3 // Try to grab the lock
Else goto 2.1 // Wait until the lock is released
3. Do something...
4. lock = 0 // Release the lock
Когда регистр ссылки сбрасывается, если другой поток предположил, что блокировка свободна, он не сможет снова записать увеличенное значение в блокировку. Таким образом, достигается одновременный доступ к переменной lock .
Разница в ядре между критическими и атомными исходит из того, что:
Зачем использовать блокировки (новую переменную), в то время как мы можем использовать фактическую переменную (над которой мы выполняем операцию), в качестве переменной блокировки?
Использование новой переменной для блокировок приведет к критической секции , в то время как использование переменной actual в качестве блокировки приведет к атомная концепция. Критическая секция полезна, когда мы выполняем много вычислений (более одной строки) для фактической переменной. Это потому, что, если результат этих вычислений не может быть записан на фактическую переменную, вся процедура должна быть повторена для вычисления результатов. Это может привести к низкой производительности по сравнению с ожиданием снятия блокировки перед входом в область с высокой вычислительной мощностью. Таким образом, рекомендуется использовать директиву atomic всякий раз, когда вы хотите выполнить одно вычисление (x ++, x--, ++ x, --x и т. Д.) И использовать критический директива, когда интенсивный раздел выполняет более сложную вычислительную область.