Что и почему рекурсивного мьютекса не должно быть такой сложной вещью, описанной в принятом ответе.
Я хотел бы записать свое понимание после некоторых поисков в сети.
Во-первых, вы должны понимать, что, говоря о мьютексах , определенно подразумевается и концепция многопоточности. (мьютекс используется для синхронизации. Мне не нужен мьютекс, если в моей программе только 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_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_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, поэтому вы определенно заблокируете мьютекс в рекурсии. Использование обычного мьютекса вызывает взаимоблокировки. И для решения этой проблемы изобретен ресурсный мьютекс .
См. Пример из принятого ответа.
Когда использовать рекурсивный мьютекс? .
Википедия очень хорошо объясняет рекурсивный мьютекс. Определенно стоит прочитать. Википедия: Reentrant_mutex