4.2.2. Неотменяемые потоки

4.2.2. Неотменяемые потоки

Поток может вообще отказаться удаляться, вызвав функцию pthread_setcancelstate(). Как и в случае функции pthread_setcanceltype(), это оказывает влияние только на вызывающий поток. Первый аргумент функции должен быть PTHREAD_CANCEL_DISABLE, если нужно запретить отмену потока, и PTHREAD_CANCEL_ENABLE в противном случае. Второй аргумент — это указатель на переменную, в которую записывается предыдущее состояние потока.

Вот как можно запретить отмену потока:

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

Функция pthread_setcancelstate() позволяет организовывать критические секции. Критической секцией называется участок программы, который должен быть либо выполнен целиком, либо вообще не выполнен. Другими словами, если поток входит в критическую секцию, он во что бы то ни стало должен дойти до ее конца.

Предположим, к примеру, что для банковской программы требуется написать функцию, осуществляющую перевод денег с одного счета на другой. Для этого нужно добавить заданную сумму на баланс одного счета и вычесть аналогичную сумму с баланса другого счета. Если между этими двумя операциями произойдет отмена потока, выполняющего функцию, программа ложно увеличит суммарный депозит банка вследствие незавершенной транзакции. Чтобы этого не случилось, обе операции должны выполняться в критической секции.

В листинге 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,

 float dollars) {

 int old_cancel_state;

 /* Проверяем баланс на счету FROM_ACCT. */

 if (account_balances(from_acct) < dollars)

  return 1;

 /* Начало критической секции. */

 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state);

 /* переводим деньги. */

 account_balances[to_acct] += dollars;

 account_balances[from_acct] -= dollars;

 /* Конец критической секции. */

 pthread_setcancelstate(old_cancel_state, NULL);

 return 0;

}

Обратите внимание на то, что по окончании критической секции восстанавливается предыдущее состояние потока, а не режим PTHREAD_CANCEL_ENABLE. Это позволит безопасно вызывать функцию process_transaction() из другой критической секции.