#include <pthread.h>
#include <stdio.h>
/* Параметры для функции char_print(). */
struct char_print_parms {
/* Отображаемый символ. */
char character;
/* Сколько раз его нужно отобразить. */
int count;
};
/* Запись указанного числа символов в поток stderr. Аргумент
PARAMETERS является указателем на структуру char_print_parms. */
void* char_print(void* parameters) {
/* Приведение указателя к нужному типу. */
struct char_print_parms* p =
(struct char_print_parms*)parameters;
int i;
for (i = 0; i < p->count; ++i)
fputc(p->character, stderr);
return NULL;
}
/* Основная программа. */
int main() {
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Создание нового потока, отображающего 30000
символов 'x'. */
thread1_args.character = 'x';
thread1_args.count = 30000;
pthread_create(&thread1_id, NULL, &char_print, &thread1_args);
/* Создание нового потока, отображающего 20000
символов 'o'. */
thread2_args.character = 'o';
thread2_args.count = 20000;
pthread_create(&thread2_id, NULL, &char_print, &thread2_args);
return 0;
}
Но постойте! Приведенная программа имеет серьезную ошибку. Основной поток (выполняющий функцию main()) создает структуры thread1_args и thread2_args в виде локальных переменных, а затем передает указатели на них дочерним потокам. Что мешает Linux распланировать работу потоков так, чтобы функция main() завершилась до того, как будут завершены другие два потока? Ничего! Но если это произойдет, структуры окажутся удаленными из памяти, хотя оба потока все еще ссылаются на них.
4.1.2. Ожидание завершения потоков
Одно из решений описанной выше проблемы заключается в том, чтобы заставить функцию main() дождаться завершения обоих потоков. Нужна лишь функция наподобие wait(), которая работает не с процессами, а с потоками. Такая функция называется pthread_join(). Она принимает два аргумента: идентификатор ожидаемого потока и указатель на переменную void*, в которую будет записано значение, возвращаемое потоком. Если последнее не важно, задайте в качестве второго аргумента NULL.
В листинге 4.3 приведена исправленная версия функции main() из предыдущего, неправильного примера. В данном случае функция main() не завершается, пока оба дочерних потока не выполнят свои задания и не перестанут ссылаться на переданные им структуры.
main() из файла thread-create.cint main() {
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Создание нового потока, отображающего 30000
символов 'x'. */
thread1_args.character = 'x';
thread1_args.count = 30000;
pthread_create(&thread1_id, NULL, &char_print, &thread1_args);
/* Создание нового потока, отображающего
20000 символов 'o'. */
thread2_args.character = 'o';
thread2_args.count = 20000;
pthread_create(&thread2_id, NULL, &char_print, &thread2_args);
/* Убеждаемся, что завершился первый поток. */
pthread_join(thread1_id, NULL);
/* Убеждаемся, что завершился второй поток. */
pthread_join(thread2_id, NULL);
/* Теперь можно спокойно завершать работу. */
return 0;
}
Мораль сей басни такова: убедитесь, что любые данные, переданные потоку по ссылке, не удаляются (даже другим потоком) до тех пор, пока поток не завершит свою работу с ними. Это относится как к локальным переменным, удаляемым автоматически при выходе за пределы своей области видимости, так и к динамическим переменным, удаляемым с помощью функции free() (или оператора delete в C++).