Чаще всего взаимоблокировка возникает, когда группа потоков пытается захватить один и тот же набор объектов. Рассмотрим, к примеру, программу, в которой два потока, выполняющих разные потоковые функции, должны захватить одни и те же два исключающих семафора. Предположим, ноток А захватывает сначала семафор 1, а затем семафор 2, в то время как поток Б захватывает семафоры в обратном порядке. Возможна достаточно неприятная ситуация, когда после захвата семафора 1 потоком А операционная система активизирует поток Б, который захватит поток 2. Далее оба потока окажутся заблокированными, так как им будет закрыт доступ к семафорам друг друга.
Это пример более общей проблемы взаимоблокировки, которая касается не только объектов синхронизации, таких как исключающие семафоры, но и ряда других ресурсов, в частности блокировок файлов и устройств. Проблема возникает, когда потоки пытаются захватить один и тот же набор ресурсов, но в разной последовательности. Выход заключается в том, чтобы обеспечить согласованный протокол доступа к ресурсам во всех потоках.
4.5. Реализация потоков в Linux
Потоковые функции, соответствующие стандарту POSIX, реализованы в Linux не так, как в большинстве других версий UNIX. Суть в том, что в Linux потоки реализованы в виде процессов. Когда вызывается функция pthread_create()
fork()
. Он, в частности, делит общее адресное пространство и ресурсы с исходным процессом, а не получает их копии.Сказанное иллюстрирует программа thread-pid
getpid()
и создает новый поток, в котором тоже выводится значение идентификатора, после чего оба потока входят в бесконечный цикл.#include
#include
#include
void* thread_function(void* arg) {
fprintf(stderr, "child thread pid is %d\n", (int) getpid());
/* Бесконечный цикл. */
while (1);
return NULL;
}
int main() {
pthread_t thread;
fprintf(stderr, "main thread pid is %d\n", (int)getpid());
pthread_create(&thread, NULL, &thread_function, NULL);
/* Бесконечный цикл. */
while (1);
return 0;
}
Запустите программу в фоновом режиме, а затем вызовите команду ps x
thread-pid
, так как она потребляет ресурсы процессора. Вот что мы получим:% cc thread-pid.c -о thread-pid -lpthread
% ./thread-pid &
[1] 14608
main thread pid is 14608
child thread pid is 14610
% ps x
PID TTY STAT TIME COMMAND
14042 pts/9 S 0:00 bash
14068 pts/9 R 0:01 ./thread-pid
14069 pts/9 S 0:00 ./thread-pid
14610 pts/9 R 0:01 ./thread-pid
14611 pts/9 R 0:00 ps x
% kill 14608
[1]+ Terminated ./thread-pid
Строки, начинающиеся с записи [1]
Обратите внимание на то, что программе thread-pid
thread_function()
. Что же такое тогда второй поток, с идентификатором 14609? Это "управляющий поток", являющийся частью внутреннего механизма реализации потоков в Linux. Он создается, когда программа вызывает функцию pthread_create()
.4.5.1. Обработка сигналов