Вы предполагаете, что операции являются атомами c. Теперь предположим, что это не так, как в реальных системах.
Будучи глобальной переменной, к count
могут обращаться все потоки в системе. Это означает, что без синхронизации все потоки будут выполнять следующие операции недетерминированно c и с чередованием:
for(int i = 0; i < 1000; i++){
aux = count;
aux++;
usleep(random() % 10);
count = aux;
}
Подводя итог, на каждой итерации l oop каждый поток будет иметь копию значения счетчика в данный момент времени, увеличит эту копию (aux++
), а затем счетчику будет присвоено локальное значение count = aux;
.
Проблема 1: Значение count
чтение каждым потоком может изменяться в разных потоках, поскольку потоки выполняются, потому что один поток может считывать значение, которое изменяется другим потоком (или несколькими) в момент времени сразу после (помните, что операции не являются атомами c и могут быть выполнены с чередованием).
Проблема 2: Значение, назначенное для count, не защищено никаким механизмом блокировки, что означает, что несколько потоков могут выполнять эту инструкцию с перемежением или даже одновременно (например, в многопроцессорной системе это возможно). Это означает, что один из выполняющихся потоков (который вы не знаете, что это) установит значение count
в aux
в count = aux
.
Простой пример возможного сценария выполнения :
Например, предположим, три потока. Поток 1 читает значение count = 100 и выгружается. Поток 2 считывает значение 100 и выполняет в течение некоторого времени установку счетчика (скажем, 300), а затем он выгружается. Наконец, поток 3 читает значение 300 и выполняет несколько итераций l oop. Если поток 1 выполняется снова и устанавливает значение count = aux
, после одной итерации l oop значение будет установлено равным 101. Смотрите проблему!
Необходима синхронизация, чтобы убедиться, что только один поток выполняет чтение, приращение и присваивание, фактически, чтобы операции вели себя так, как если бы они были атомами c.
Q: Если я поставлю семафор, sem_wait
перед для l oop, и sem_post
после для l oop, означает ли это, что мои потоки больше не работают параллельно?
A: Это означает, что каждый поток будет чередовать выполнение для l oop. Например, поток 1 выполнит, скажем, 100 итераций для l oop, поток 2 выполнит 200 итераций и т. Д. c. Помните: планировщик контролирует выполнение каждого потока и, следовательно, количество итераций не контролируется пользователем. Ваш код синхронизирован, но не идеальным образом.
В: Куда мне поместить sem_wait
и sem_post
, чтобы мои потоки были правильно синхронизированы?
A : Вы должны использовать семафоры для наименьшего числа возможных операций, которые требуют синхронизации, чтобы извлечь максимальную выгоду из параллельного / параллельного выполнения кода. Например, используя семафоры, ваш код может быть:
for(int i = 0; i < 1000; i++){
sem_wait(...);
count++;
sem_post(...);
}
Вам больше не нужно aux
, так как семафор гарантирует, что только один поток увеличивает значение count
.
Обратите внимание: поскольку вы используете потоки, вы также можете использовать мьютексы вместо семафоров.
Надеюсь, это прояснит ваши сомнения.