Почему простое увеличение числа может испортить данные

Представим общий счётчик со значением 10. Операция «увеличить на единицу» обычно состоит из нескольких шагов: поток читает число, прибавляет единицу и записывает результат. Если два потока выполняют эти шаги одновременно, оба могут прочитать 10 и затем записать 11. Одно увеличение потеряется — хотя каждый поток выполнил свою операцию.

Замок охраняет не переменную, а участок кода

Мьютекс — это механизм взаимного исключения: поток сначала пытается захватить замок, выполняет защищённую часть программы, а затем освобождает его. Пока замок занят, другой поток ждёт. Для счётчика защищённым участком будут чтение, изменение и запись: вместе эти действия становятся последовательными, и второй поток увидит уже обновлённое значение.

Важно, что мьютекс сам по себе не «прикреплён» к переменной. Защита работает, только если все потоки, которые обращаются к общим данным, используют один и тот же замок и соблюдают порядок захвата и освобождения. Если один участок кода обходит мьютекс, гонка данных всё ещё возможна.

Защита имеет цену

Ожидание упорядочивает работу, но может замедлить программу: пока один поток держит замок, остальные не могут выполнять защищённый участок. Поэтому под замком стараются оставлять как можно меньше действий. А если потоки захватили несколько замков в разном порядке, каждый может ждать освобождения замка, удерживаемого другим, — так возникает взаимная блокировка.

Мьютекс не делает параллельную работу быстрее; он позволяет согласовать доступ к общему состоянию. Цена этого порядка — возможное ожидание, а выгода — корректный результат вместо случайно потерянного обновления.