/* Дожидаться завершения второго потока нет необходимости. */
return 0;
}
Даже если поток был создан ожидаемым, его позднее можно сделать отсоединенным. Для этого нужно вызвать функцию
pthread_detach
. Обратное преобразование невозможно.
4.2.
Отмена потока
Обычно поток завершается при выходе из потоковой функции или вследствие вызова функции
pthread_exit
. Но существует возможность запросить из одного потока уничтожение другого. Это называется отменой, или принудительным завершением, потока.
Чтобы отменить поток, вызовите функцию
pthread_cancel
, передав ей идентификатор требуемого потока. Далее можно дождаться завершения потока. Вообще-то, это обязательно нужно делать с целью освобождения ресурсов, если только поток не является отсоединенным. Отмененный поток возвращает специальное значение
PTHREAD_CANCELED
.
Во многих случаях поток выполняет код, который нельзя просто взять и прервать. Например, поток может выделить какие-то ресурсы, поработать с ними, а затем удалить. Если отмена потока произойдет где-то посередине, освободить занятые ресурсы станет невозможно, вследствие чего они окажутся потерянными для системы. Чтобы учесть эту ситуацию, поток должен решить, где и когда он может быть отменен.
С точки зрения возможности отмены поток находится в одном из трех состояний.
■ Асинхронно отменяемый. Такой поток можно отменить в любой точке его выполнения.
■ Синхронно отменяемый. Поток можно отменить, но не везде. Запрос на отмену помещается в очередь, и поток отменяется только по достижении определенной точки.
■ Неотменяемый. Попытки отменить поток игнорируются. Первоначально поток является синхронно отменяемым.
4.2.1. Синхронные и асинхронные потоки
Асинхронно отменяемый поток "свободен" в любое время. Синхронно отменяемый поток, наоборот, бывает "свободным", только когда ему "удобно". Соответствующие места в программе называются точками отмены. Запрос на отмену помещается в очередь и находится в ней до тех пор, пока поток не достигнет следующей точки отмены.
Чтобы сделать поток асинхронно отменяемым, воспользуйтесь функцией
pthread_setcanceltype
. Эта функция влияет на тот поток, в котором она была вызвана. Первый ее аргумент должен быть
PTHREAP_CANCEL_ASYNCHRONOUS
в случае асинхронных потоков и
PTHREAD_CANCEL_DEFERRED
— в случае синхронных потоков. Второй аргумент — это указатель на переменную, в которую записывается предыдущее состояние потока.
Что такое точка отмены и где она должна находиться? На этот вопрос нельзя дать прямой ответ. Точка отмены создается с помощью функции
pthread_testcancel
. Все, что она делает, — это обрабатывает отложенный запрос на отмену в синхронном потоке. Ее следует периодически вызывать в потоковой функции в ходе длительных вычислений, там, где
поток можно завершить без риска потери ресурсов или других побочных эффектов.
Некоторые функции неявно создают точки отмены. О них можно узнать на
man
– странице, посвященной функции
pthread_cancel
. Учтите, что они могут вызываться в других функциях, которые, тем самым, косвенно станут точками отмены.
4.2.2. Неотменяемые потоки
Поток может вообще отказаться удаляться, вызвав функцию
pthread_setcancelstate
. Как и в случае функции
pthread_setcanceltype
, это оказывает влияние только на вызывающий поток. Первый аргумент функции должен быть
PTHREAD_CANCEL_DISABLE
, если нужно запретить отмену потока, и
PTHREAD_CANCEL_ENABLE
в противном случае. Второй аргумент — это указатель на переменную, в которую записывается предыдущее состояние потока.
позволяет организовывать критические секции. Критической секцией называется участок программы, который должен быть либо выполнен целиком, либо вообще не выполнен. Другими словами, если поток входит в критическую секцию, он во что бы то ни стало должен дойти до ее конца.
Предположим, к примеру, что для банковской программы требуется написать функцию, осуществляющую перевод денег с одного счета на другой. Для этого нужно добавить заданную сумму на баланс одного счета и вычесть аналогичную сумму с баланса другого счета. Если между этими двумя операциями произойдет отмена потока, выполняющего функцию, программа ложно увеличит суммарный депозит банка вследствие незавершенной транзакции. Чтобы этого не случилось, обе операции должны выполняться в критической секции.
В листинге 4.6 показан пример функции
process_transaction
, осуществляющей данную задумку. Функция запрещает отмену потока до тех пор, пока баланс обоих счетов не будет изменен.
Листинг 4.6. (critical_section.c) Защита банковской транзакции с помощью критической секции
#include <pthread.h>
#include <stdio.h>
#include <string.h>
/* Массив балансов счетов, упорядоченный по номеру счета. */
float* account_balances;
/* перевод денежной суммы, равной параметру DOLLARS, со счета
FROM_ACCT на счет TO_ACCT. Возвращается 0, если транзакция
завершена успешно, или 1, если баланс счета FROM_ACCT
слишком мал. */
int process_transaction(int from_acct, int to_acct,