void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
Вы передаете адрес объекта типа pthread_t
, который можете применять в дальнейшем для ссылки на поток. Вы не хотите менять атрибуты потока, заданные по умолчанию, поэтому во втором параметре передаете NULL
. Последние два параметра — вызываемая функция и передаваемый ей параметр.
Если вызов завершился нормально, теперь выполняются два потока. Исходный поток ( main
) продолжается и выполняет код, расположенный следом за функцией pthread_create
, а новый поток начинает выполнение в функции, образно названной thread_function
.
Исходный поток проверяет, запустился ли новый поток, и затем вызывает функцию pthread_join
:
res = pthread_join(a_thread, &thread_result);
Здесь вы передаете идентификатор потока, который ждете, чтобы присоединить, и указатель на результат. Эта функция, прежде чем вернуть управление, будет ждать, пока другой поток не завершится. Затем она выводит возвращаемое из потока значение и содержимое переменной и завершается.
Новый поток начинает выполнение, запуская функцию thread_function
, которая выводит свои аргументы, засыпает на короткий период, обновляет глобальные переменные и затем завершается, возвращая строку в поток main
. Новый поток пишет в тот же массив message
, к которому у исходного потока есть доступ. Если бы вы вызвали функцию fork
вместо pthread_create
, массив представлял бы собой копию массива message
, а не сам массив.
В упражнении 12.2 показано, как написать программу, которая проверяет одновременное выполнение двух потоков. (Вы, конечно, применяете однопроцессорную систему, ЦП будет искусно переключаться между потоками, а не одновременно выполнять оба потока, используя отдельные ядра процессора аппаратными средствами.) Поскольку вы не встречались еще с какими-либо функциями синхронизации потоков, это будет очень неэффективная программа, делающая нечто, именуемое опросом (polling) двух потоков. И снова вы воспользуетесь тем, что все, за исключением локальных переменных функции, совместно используется двумя потоками в процессе.
Упражнение 12.2. Одновременное выполнение двух потоков
Программа thread2.c в этом упражнении создается за счет небольших изменений программы thread1.c. Вы добавите дополнительную глобальную переменную для определения выполняющегося потока.
Примечание
Файлы с полными текстами примеров можно загрузить с Web-сайта книги.
int run_now = 1;
Задайте run_now
равной 1, когда выполняется функция main
, и 2, когда выполняется новый поток.
В функцию main
после создания нового потока добавьте следующий код:
int print_count1 = 0;
while (print_count1+ < 20) {
if (run_now == 1) {
printf("1");
run_now = 2;
} else {
sleep(1);
}
}
Если переменная run_now
равна 1, выведите "1" и присвойте переменной значение 2. В противном случае вы на короткое время засыпаете и снова проверяете значение. Вы ждете, пока значение изменится на 1, проверяя время от времени снова. Этот прием называется циклам активного или деятельного ожидания (busy wait), несмотря, на то, что в данном случае программа засыпает на секунду между очередными проверками. Позже в этой главе вы увидите, как сделать это лучше.
В функции 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
Как это работает
Читать дальше