Прежде всего, я знаю, что lock {}
- это синтетический сахар для Класс монитора
. (о, синтаксический сахар)
Я играл с простыми проблемами многопоточности и обнаружил, что не могу полностью понять, как блокировка некоторого произвольного СЛОВА памяти защищает от кэширования целую другую память - регистры / кеш процессора и т. д. проще использовать примеры кода, чтобы объяснить то, о чем я говорю:
for (int i = 0; i < 100 * 1000 * 1000; ++i) {
ms_Sum += 1;
}
В конце концов ms_Sum
будет содержать 100000000
, что, конечно, ожидается.
Теперь мы стареем. собираюсь выполнить тот же цикл, но на 2 разных потоках и с уменьшенным вдвое верхним пределом.
for (int i = 0; i < 50 * 1000 * 1000; ++i) {
ms_Sum += 1;
}
Из-за отсутствия синхронизации мы получаем неверный результат - на моей 4-ядерной машине это почти случайное число 52 388 219
, которое является чуть больше половины от 100 000 000
. Если мы заключим ms_Sum + = 1;
в lock {}
, мы, естественно, получим абсолютно правильный результат 100 000 000
. Но что меня интересует (честно говоря, я был ожидал подобного поведения), добавление блокировки
до или после ms_Sum + = 1;
строка дает ответ почти правильно:
for (int i = 0; i < 50 * 1000 * 1000; ++i) {
lock (ms_Lock) {}; // Note curly brackets
ms_Sum += 1;
}
В этом случае я обычно получаю ms_Sum = 99 999 920
, что очень близко.
Вопрос: почему именно блокировка (ms_Lock) {ms_Counter + = 1; }
делает программу полностью правильной, но lock (ms_Lock) {}; ms_Counter + = 1;
только почти правильно; как блокировка произвольной переменной ms_Lock
делает всю память стабильной?
Большое спасибо!
P.S. Ушел читать книги о многопоточности.
ПОДОБНЫЙ ВОПРОС (S)
Как оператор блокировки обеспечивает внутрипроцессорную синхронизацию?
Синхронизация потоков. Почему именно этой блокировки недостаточно для синхронизации потоков