Хорошо, рассмотрим следующий код:
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int num = 2;
void lock_and_call_again() {
pthread_mutex_lock(&mutex);
if(num > 0) {
--num;
lock_and_call_again();
}
}
int main(int argc, char** argv) {
lock_and_call_again();
}
(скомпилируйте с gcc -pthread thread-test.c
, если вы сохраните код как thread-test.c
)
Это явно однопоточный, не так ли?не так ли?Тем не менее, он войдет в тупик, потому что вы пытаетесь заблокировать уже заблокированный мьютекс.
Это в основном то, что подразумевается в приведенном вами абзаце, ИМХО:
Это ненезависимо от того, выполняется ли это в нескольких потоках или в одном потоке, если вы попытаетесь заблокировать уже заблокированный мьютекс, ваша программа завершится взаимоблокировкой.
Если функция вызывает себя, как lock_and_call
вышеэто то, что называется рекурсивный вызов .
Как объясняет Джеймс Большой, сигнал может появиться в любое время, и если обработчик сигнала зарегистрирован с этим сигналом, он будет вызываться непредсказуемовремя, если никакие меры не предпринимаются, даже когда тот же обработчик уже выполняется - что приводит к некоему неявному рекурсивному выполнению обработчика сигнала.
Если этот обработчик получает какой-то видблокировка, вы попадаете в тупик, даже без функции, вызывающей себя явно.Рассмотрим следующую функцию:
pthread_mutex_t mutex;
void my_handler(int s) {
pthread_mutex_lock(&mutex);
sleep(10);
pthread_mutex_unnlock(&mutex);
}
Теперь, если вы зарегистрируете эту функцию для определенного сигнала, она будет вызываться всякий раз, когда сигнал отлавливается вашей программой.Если обработчик был вызван и спит, он может быть прерван, обработчик снова вызван, и обработчик попытается заблокировать уже заблокированный mutex
.
Относительно формулировки цитаты:
" Однопоточная программа такого типа может иметь одну и ту же подпрограмму в различных фреймах вызова в своем стеке процессов. "
Когда вызывается функция, некоторая информацияхранится в стеке процесса - например, адрес возврата.Эта информация называется фреймом вызова.Если вы вызываете функцию рекурсивно, как в примере выше, эта информация несколько раз сохраняется в стеке - сохраняется несколько фреймов вызова.Это признано немного неуклюжим, я признаю ...