Прежде всего, я знаю, что lock{}
синтетический сахар для Monitor
класса. (о, синтаксический сахар)
Я играл с простыми проблемами многопоточности и обнаружил, что не могу полностью понять, как блокировка некоторого произвольного СЛОВА памяти защищает всю другую память от кеширования, это регистры / кеш процессора и т. Д. Легче использовать образцы кода, чтобы объяснить, о чем я говорю:
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
. Но что меня интересует (честно говоря, я ожидал аналогичного поведения), добавление строки lock
до или после 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
, что очень близко.
Вопрос: почему именно lock(ms_Lock) { ms_Counter += 1; }
делает программу полностью правильной, а lock(ms_Lock) {}; ms_Counter += 1;
только почти правильной; как блокировка произвольной ms_Lock
переменной делает всю память стабильной?
Большое спасибо!
P.S. Читал книги о многопоточности.
ПОДОБНЫЕ ВОПРОСЫ
Как оператор блокировки обеспечивает синхронизацию внутри процессора?
Синхронизация потоков. Почему именно этой блокировки недостаточно для синхронизации потоков