В листинге 4.3 приведена исправленная версия функции main()
из предыдущего, неправильного примера. В данном случае функция main()
не завершается, пока оба дочерних потока не выполнят свои задания и не перестанут ссылаться на переданные им структуры.
Листинг 4.3. Исправленная функция main()
из файла thread-create.c
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);
/* Убеждаемся, что завершился первый поток. */
pthread_join(thread1_id, NULL);
/* Убеждаемся, что завершился второй поток. */
pthread_join(thread2_id, NULL);
/* Теперь можно спокойно завершать работу. */
return 0;
}
Мораль сей басни такова: убедитесь, что любые данные, переданные потоку по ссылке, не удаляются ( даже другим потоком ) до тех пор, пока поток не завершит свою работу с ними. Это относится как к локальным переменным, удаляемым автоматически при выходе за пределы своей области видимости, так и к динамическим переменным, удаляемым с помощью функции free()
(или оператора delete
в C++).
4.1.3. Значения, возвращаемые потоками
Если второй аргумент функции pthread_join()
не равен NULL, то в него помещается значение, возвращаемое потоком. Как и потоковый аргумент, это значение имеет тип void*
. Если поток возвращает обычное число типа int
, его можно свободно привести к типу void*
, а затем выполнить обратное преобразование по завершении функции pthread_join()
. [13] Данный способ не является стандартным. В обязанности программиста входит убедиться, что в процессе подобных преобразований не произойдет потеря значащих разрядов.
Программа, представленная в листинге 4.4, в отдельном потоке вычисляет n -е простое число и возвращает его в программу. Тем временем функция main()
может продолжать свои собственные вычисления. Сразу признаемся: алгоритм последовательного деления, используемый в функции compute_prime()
, весьма неэффективен. В книгах по численным методам описаны более мощные алгоритмы (например, "решето Эратосфена").
Листинг 4.4. ( primes.с ) Вычисление простых чисел в потоке
#include
#include
/* Находим простое число с порядковым номером N, где N -- это
значение, на которое указывает параметр ARG. */
void* compute_prime(void* arg) {
int candidate = 2;
int n = *((int*)arg);
while (1) {
int factor;
int is_prime = 1;
/* Проверка простого числа путем последовательного деления. */
for (factor = 2; factor < candidate; ++factor)
if (candidate % factor == 0) {
is_prime = 0;
break;
}
/* Это то простое число, которое нам нужно? */
if (is_prime) {
if (--n == 0)
/* Возвращаем найденное число в программу. */
return (void*)candidate;
}
++candidate;
}
return NULL;
}
int main() {
pthread_t thread;
int which_prime = 5000;
int prime;
/* Запускаем поток, вычисляющий 5000-е простое число. */
pthread_create(&thread, NULL, &compute_prime, &which_prime);
/* Выполняем другие действия. */
/* Дожидаемся завершения потока и принимаем возвращаемое им
значение. */
pthread_join(thread, (void*)&prime);
/* Отображаем вычисленный результат. */
printf("The %dth prime number is %d.\n", which_prime, prime);
return 0;
}
4.1.4. Подробнее об идентификаторах потоков
Иногда в программе возникает необходимость определить, какой поток выполняет ее в данный момент. Функция pthread_self()
возвращает идентификатор потока, в котором она вызвана. Для сравнения двух разных идентификаторов предназначена функция pthread_equal()
.
Эти функции удобны для проверки соответствия заданного идентификатора текущему потоку. Например, поток не должен вызывать функцию pthread_join()
, чтобы ждать самого себя (в подобной ситуации возвращается код ошибки EDEADLK
). Избежать этой ошибки позволяет следующая проверка:
if (!pthread_equal(pthread_self(), other_thread)) pthread_join(other_thread, NULL);
Читать дальше