Что и почему рекурсивный мьютекс не должен быть такой сложной вещью, описанной в принятом ответе.
Я бы хотел записать свое понимание после того, как покопался в сети.
Во-первых, вы должны понимать, что когда речь идет о мьютекс , определенно участвуют также многопоточные концепции. (мьютекс используется для синхронизации. Мне не нужен мьютекс, если в моей программе только 1 поток)
Во-вторых, вы должны знать разницу между нормальным мьютексом и рекурсивным мьютексом .
Цитируется по APUE :
(Рекурсивный мьютекс - это) Тип мьютекса, который позволяет той же нити блокировать
несколько раз без предварительной разблокировки.
Ключевым отличием является то, что в пределах одного потока , блокировка рекурсивной блокировки не приводит к взаимоблокировке, а также не блокирует поток.
Означает ли это, что рекурсивная блокировка никогда не вызывает тупик?
Нет, он все равно может вызвать взаимную блокировку как обычный мьютекс, если вы заблокировали его в одном потоке, не разблокировав его, и попытались заблокировать его в других потоках.
Давайте посмотрим на код в качестве доказательства.
- нормальный мьютекс с тупиком
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t lock;
void * func1(void *arg){
printf("thread1\n");
pthread_mutex_lock(&lock);
printf("thread1 hey hey\n");
}
void * func2(void *arg){
printf("thread2\n");
pthread_mutex_lock(&lock);
printf("thread2 hey hey\n");
}
int main(){
pthread_mutexattr_t lock_attr;
int error;
// error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_DEFAULT);
if(error){
perror(NULL);
}
pthread_mutex_init(&lock, &lock_attr);
pthread_t t1, t2;
pthread_create(&t1, NULL, func1, NULL);
pthread_create(&t2, NULL, func2, NULL);
pthread_join(t2, NULL);
}
вывод:
thread1
thread1 hey hey
thread2
пример общего тупика, без проблем.
- рекурсивный мьютекс с тупиком
Просто раскомментируйте эту строку
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
и закомментируйте другой.
вывод:
thread1
thread1 hey hey
thread2
Да, рекурсивный мьютекс также может вызвать тупик.
- нормальный мьютекс, блокировка в том же потоке
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t lock;
void func3(){
printf("func3\n");
pthread_mutex_lock(&lock);
printf("func3 hey hey\n");
}
void * func1(void *arg){
printf("thread1\n");
pthread_mutex_lock(&lock);
func3();
printf("thread1 hey hey\n");
}
void * func2(void *arg){
printf("thread2\n");
pthread_mutex_lock(&lock);
printf("thread2 hey hey\n");
}
int main(){
pthread_mutexattr_t lock_attr;
int error;
// error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_DEFAULT);
if(error){
perror(NULL);
}
pthread_mutex_init(&lock, &lock_attr);
pthread_t t1, t2;
pthread_create(&t1, NULL, func1, NULL);
sleep(2);
pthread_create(&t2, NULL, func2, NULL);
pthread_join(t2, NULL);
}
выход:
thread1
func3
thread2
тупик в thread t1
, в func3
.
(Я использую sleep(2)
, чтобы было легче увидеть, что в первую очередь тупик вызван повторной блокировкой в func3
)
- рекурсивный мьютекс, блокировка в том же потоке
Опять же, раскомментируйте строку рекурсивного мьютекса и закомментируйте другую строку.
вывод:
thread1
func3
func3 hey hey
thread1 hey hey
thread2
тупик в thread t2
, в func2
. Увидеть? func3
завершается и завершается, повторная блокировка не блокирует поток и не приводит к тупику.
Итак, последний вопрос, зачем нам это нужно?
Для рекурсивной функции (вызывается в многопоточных программах и вы хотите защитить некоторые ресурсы / данные).
например. У вас есть многопоточная программа, и вы вызываете рекурсивную функцию в потоке A. У вас есть некоторые данные, которые вы хотите защитить в этой рекурсивной функции, поэтому вы используете механизм мьютекса. Выполнение этой функции является последовательным в потоке A, поэтому вы обязательно заблокируете мьютекс в рекурсии. Использование нормального мьютекса вызывает тупики. И Resursive Mutex изобретен, чтобы решить эту проблему.
см. Пример из принятого ответа
Когда использовать рекурсивный мьютекс? .
Википедия очень хорошо объясняет рекурсивный мьютекс. Определенно стоит почитать. Википедия: Reentrant_mutex