Проблема с параллелизмом заключается в том, что вы не можете контролировать порядок одновременного доступа. Итак, мы делаем следующие предположения:
- x - это глобальная переменная, доступная различным потокам
- x ++ и x-- выполняются независимыми потоками.
Как указано в вашей книге, выполнение операции чтения-изменения-записи в общей переменной представляет собой трехэтапный процесс:
1 / считайте var в процессор reg
2 / изменить процессор рег
3 / запись процессора в регистр
Каждое из этих отдельных действий соответствует инструкции процессора и, следовательно, является атомным . Но глобальная операция чтения-изменения-записи не разделяется и может быть разделена другими параллельными действиями.
Теперь рассмотрим параллельные операции
Thread A Thread B
1/ read x->regA a/ read x->regB
2/ regA++ b/ regB--
3/ write regA->x c/ write regB
Внутри потока порядок действий 123 или abc будет соблюдаться, но между потоками A и B возможен любой относительный порядок.
С 123abc мы получаем ожидаемый результат. x изменяется на 1 потоком A, затем читается потоком B, уменьшается и, наконец, x = 0
То же самое для abc123
Но другие заказы приведут к другим результатам.
Например: 1a2b3c
Thread A Thread B
1/ read x->regA=0
a/ read x->regB=0
2/ regA++=1
b/ regB--=-1
3/ write regA->x=1
c/ write regB->x=-1
И, наконец, х = -1.
Но при заказе 1abc23 мы получим x = 1.
Thread A Thread B
1/ read x->regA=0
a/ read x->regB=0
b/ regB--=-1
c/ write regB->x=-1
2/ regA++=1
3/ write regA->x=1
Поскольку все ордера могут быть достигнуты, результат будет \ in {-1,0,1}
Единственный способ получить детерминированный результат - использовать атомарные инструкции чтения-изменения-записи. Все современные процессоры имеют средства для этого (fetch-and-add или load-connected / store-cond), и в этом случае последовательности 123 или abc не могут быть разделены. Единственный возможный порядок будет 123abc или abc123, и оба приведут к детерминированному результату x = 0.