Если переменная run_now равна 1, выведите "1" и присвойте переменной значение 2. В противном случае вы на короткое время засыпаете и снова проверяете значение. Вы ждете, пока значение изменится на 1, проверяя время от времени снова. Этот прием называется
В функции thread_function, где выполняется ваш новый поток, вы делаете примерно то же самое, но с противоположными значениями.
int print_count2 = 0;
while (print_count2++ < 20) {
if (run_now == 2) {
printf("2");
run_now = 1;
} else {
sleep(1);
}
}
Вы удаляете переданные параметр и возвращаемое значение, т.к. они вас больше не интересуют.
Когда вы выполните программу, то увидите следующий вывод. (Вы можете обнаружить, что для формирования вывода, особенно на машине с одноядерным ЦП, программе потребуется несколько секунд.)
$ cc -D_REENTRANT thread2.с -о thread2 -lpthread
$ ./thread2
12121212121212121212
Waiting for thread to finish...
Thread joined
Как это работает
Каждый поток заставляет другой поток выполняться, задавая переменную run_now и затем ожидая, пока другой поток не изменит значение, чтобы можно было продолжить выполнение. Из программы видно, что выполнение переходит от одного потока к другому автоматическими кроме того, она демонстрирует точку, совместно используемую обоими потоками, — переменную run_now.
Синхронизация
В предыдущем разделе вы видели, что два потока выполняются одновременно, но метод переключения между ними топорный и очень неэффективный. К счастью, существует ряд функций, специально разработанных для предоставления лучших способов управления исполнением потоков и доступа к важным фрагментам кода.
В этом разделе мы рассмотрим два основных метода:
Синхронизация с помощью семафоров
Для семафоров есть два набора интерфейсных функций: один взят из POSIX Realtime Extensions (дополнения POSIX для режима реального времени) и применяется для потоков, а другой, известный как семафоры System V, обычно применяется для синхронизации процессов. (Мы обсудим второй тип в
Дейкстра, голландский ученый, специалист по компьютерным наукам, первым сформулировал идею семафоров.
В этом разделе мы рассмотрим простейший тип семафора, двоичный или бинарный семафор, который принимает только значения 0 и 1. Существует и более обобщенный вид семафора, считающий (counting) семафор, принимающий более широкий диапазон значений. Обычно семафоры используются для защиты фрагмента программного кода, так чтобы только один поток исполнения мог изменить его в любой конкретный момент времени. Для этого нужен двоичный семафор. Порой вам необходимо разрешить ограниченному числу потоков выполнять заданный фрагмент кода, для этого вам следует применять считающий семафор. Поскольку считающие семафоры гораздо менее популярны, мы не будем их обсуждать в дальнейшем, отметив лишь, что они представляют собой логическое расширение двоичного семафора и что реальные вызовы функций должны быть идентичны.