Мьютексы позволяют сделать потокобезопасной большинство функций, но это негативно сказывается на производительности, поскольку открытие и закрытие мьютексов требует определенных ресурсов. Вместо этого потоковую безопасность можно обеспечить за счет реентерабельных функций, избегая задействования глобальных и статических переменных.
Большинство функций, описанных в стандарте SUSv3, должны быть потокобезопасными. Этот стандарт также ограничивает круг функций, на которые это требование не распространяется. Такие функции обычно используют статическое хранилище для возвращения информации вызывающему потоку или сохраняют данные между последовательными вызовами. По умолчанию они не являются реентерабельными, и для обеспечения их потоков безопасности нельзя применять мьютексы. Мы рассмотрели две примерно равнозначные методики, которые позволяют сделать функцию потокобезопасной, не изменяя при этом ее интерфейс — данные уровня потока и локальное хранилище потока. Оба этих подхода позволяют функции выделять в каждом потоке отдельное постоянное хранилище.
Ознакомьтесь с источниками, приведенными в разделе 29.10.
31.1. Реализуйте функцию one_time_init(control, init), которая делает то же самое, что и вызов pthread_once(). Аргумент control должен быть указателем на статически выделенную структуру, содержащую булеву переменную и мьютекс. Булева переменная сигнализирует, вызывалась ли функция init ранее, а мьютекс защищает доступ к переменной. Чтобы не усложнять реализацию, вы можете игнорировать случаи, когда init() завершается неудачей или отменяется при первом вызове из потока (то есть в таких случаях вам не обязательно предусматривать механизм, благодаря которому следующий поток, вызывающий one_time_init(), делает повторную попытку вызова init()).
31.2. Напишите потокобезопасные версии функций dirname() и basename() (см. раздел 18.14) на основе данных уровня потока.
32. Потоки выполнения: отмена потока
Обычно разные потоки работают параллельно, выполняя свою задачу до тех пор, пока не решат завершиться с помощью вызова pthread_exit() или вернуться из своей начальной функции.
Но иногда возникает необходимость в
В этой главе мы рассмотрим механизм отмены POSIX-потоков.
Функция pthread_cancel() отправляет заданному потоку thread запрос отмены.
#include
int pthread_cancel(pthread_t
Возвращает 0 при успешном завершении или положительное число, если произошла ошибка
Выполнив запрос отмены, функция pthread_cancel() немедленно возвращается; то есть она не ждет завершения заданного потока.
Что именно происходит с заданным потоком и в какой момент, зависит от его состояния и типа отмены (о чем пойдет речь в следующем разделе).
Функции pthread_setcancelstate() и pthread_setcanceltype() устанавливают флаги, которые позволяют управлять реакцией потока на запрос отмены.
#include
int pthread_setcancelstate(int
int pthread_setcanceltype(int
Обе функции возвращают 0 при успешном завершении или положительное число, если произошла ошибка
Функция pthread_setcancelstate() приводит состояние отмены вызывающего потока к одному из двух значений, поддерживаемых аргументом state.
• PTHREAD_CANCEL_DISABLE — поток нельзя отменить. Если получен запрос отмены, он откладывается до момента, когда возможность отмены будет включена.
• PTHREAD_CANCEL_ENABLE — поток можно отменить. Это состояние используется по умолчанию для создаваемых потоков.
Предыдущее состояние отмены возвращается по адресу, на который указывает аргумент oldstate.
Если нас не интересует предыдущее состояние отмены, Linux позволяет присвоить oldstate значение NULL. Это также относится ко многим другим системам; однако стандарт SUSv3 не предусматривает данной возможности, поэтому переносимые приложения не могут на нее полагаться. Для аргумента oldstate всегда нужно указывать ненулевое значение.
Временное отключение возможности отмены (PTHREAD_CANCEL_DISABLE) может пригодиться, если поток находится на участке кода, который должен быть выполнен