Что std :: mutex предотвращает изменение потоков? - PullRequest
1 голос
/ 05 февраля 2020

Какая часть памяти блокируется mutex, когда .lock() или .try_lock(), это только функция или целая программа, которая блокируется?

Ответы [ 4 ]

3 голосов
/ 05 февраля 2020

Мьютекс не блокирует ничего, кроме себя самого. Вы можете думать о мьютексе как о воротах, где вы можете открыть его только изнутри. Когда ворота заблокированы, любой поток, который пытается заблокировать мьютекс, будет сидеть у ворот и ждать, пока текущий поток, который находится за воротами, разблокирует его и впустит их. Когда они не заблокированы, то когда вы вызываете lock Вы можете просто go войти, закрыть и заблокировать ворота, и теперь никакие потоки не могут пройти мимо ворот, пока вы не разблокируете их и не впустите их.

3 голосов
/ 05 февраля 2020

Ничто не заблокировано, кроме мьютекса. Все остальное продолжает работать (пока не попытается заблокировать уже заблокированный мьютекс). Мьютекс существует только для того, чтобы два потока не могли запустить код между блокировкой мьютекса и разблокировкой мьютекса одновременно.

1 голос
/ 05 февраля 2020

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

Вы могли бы думать о мьютексе как о чем-то вроде логического okToModify. Всякий раз, когда вы хотите что-то отредактировать, вы проверяете, является ли okToModify true. Если это так, вы установите его на false (не позволяя другим потокам изменять его), измените его, затем установите okToModify обратно на true, чтобы сообщить другим потокам, что вы закончили, и дать им возможность изменить :

// WARNING! This code doesn't actually work as a lock!
//    it is just an example of the concept.
struct LockedInt {
    bool okToModify; // This would be your mutex instead of a bool.
    int integer;
};

struct LockedInt myLockedInt = { true, 0 };    

...

while (myLockedInt.okToModify == false)
    ; // wait doing nothing until whoever is modifying the int is done.
myLockedInt.okToModify = false; // Prevent other threads from getting out of while loop above.
myLockedInt.integer += 1;
myLockedInt.okToModify = true; // Now other threads get out of the while loop if they were waiting and can modify.

Тогда как l oop и okToModify = false выше - это, в основном, блокировка мьютекса, а okToModify = true - это разблокировка мьютекса.

Теперь, почему нам нужны мьютексы и мы не используем логические выражения? Потому что поток может быть запущен одновременно с этими тремя строками выше. Код для блокировки мьютекса фактически гарантирует, что ожидание okToModify становится true и установка okToModify = false происходят в одном go, и поэтому никакой другой поток не может попасть "между строк", например, используя специальная инструкция машинного кода, называемая «сравнить и обменять».

Так что do not использует булевы значения вместо мьютексов , но вы можете думайте о мьютексе как о специальном, поточно-ориентированном логическом значении.

0 голосов
/ 05 февраля 2020

m.lock() на самом деле не блокирует ничего. Что он делает, он ждет , чтобы получить право владения мьютекса. Мьютекс всегда либо принадлежит только одному потоку, либо доступен . m.lock() ждет, пока мьютекс станет доступным, и затем он переходит во владение им от имени вызывающего потока.

m.unlock освобождает мьютекс (т. Е. Он отказывается от владения) , в результате чего мьютекс снова становится доступным.


Мьютексы также выполняют еще одну очень важную функцию. В современном C ++, когда какой-то поток T выполняет последовательность назначений различных значений в различные области памяти, система не дает никаких гарантий относительно того, когда другие потоки U, V и W увидят эти назначения, будут ли другие потоки выполнять назначения. в том же порядке, в котором их выполнял поток T, или даже, будут ли другие потоки когда-либо видеть назначения.

Существуют довольно сложные правила, управляющие вещами, которые может сделать программист убедитесь, что разные потоки видят единообразное представление объектов разделяемой памяти (Google "модель памяти C ++"), но вот одно простое правило:

Независимо от того, что поток T делал перед тем, как освободить мьютекс M, он гарантированно будет виден любому другой поток U после поток U впоследствии блокирует тот же мьютекс M.

...