O quê e por quê do mutex recursivo não deve ser algo tão complicado descrito na resposta aceita.
Eu gostaria de escrever meu entendimento depois de vasculhar a rede.
Primeiro, você deve perceber que, ao falar sobre mutex , os conceitos de multithread também estão definitivamente envolvidos. (mutex é usado para sincronização. Eu não preciso de mutex se eu tiver apenas 1 thread em meu programa)
Em segundo lugar, você deve saber a diferença entre um mutex normal e um mutex recursivo .
Citado de APUE :
(Um mutex recursivo é a) Um tipo de mutex que permite que o mesmo segmento o bloqueie várias vezes sem primeiro desbloqueá-lo.
A principal diferença é que dentro do mesmo encadeamento , travar novamente um bloqueio recursivo não leva a um deadlock, nem bloqueia o encadeamento.
Isso significa que o bloqueio recusivo nunca causa um impasse?
Não, ele ainda pode causar um deadlock como um mutex normal se você bloqueá-lo em um thread sem desbloqueá-lo e tentar bloqueá-lo em outros threads.
Vamos ver alguns códigos como prova.
- mutex normal com deadlock
#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);
}
resultado:
thread1
thread1 hey hey
thread2
exemplo de deadlock comum, sem problema.
- mutex recursivo com deadlock
Apenas descomente esta linha
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
e comente a outra.
resultado:
thread1
thread1 hey hey
thread2
Sim, mutex recursivo também pode causar conflito.
- mutex normal, relock no mesmo segmento
#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);
}
resultado:
thread1
func3
thread2
Impasse em thread t1
, em func3
.
(Eu costumo sleep(2)
tornar mais fácil ver que o impasse é causado primeiramente pelo relocking func3
)
- mutex recursivo, relock no mesmo segmento
Novamente, descomente a linha mutex recursiva e comente a outra linha.
resultado:
thread1
func3
func3 hey hey
thread1 hey hey
thread2
Impasse em thread t2
, em func2
. Vejo? func3
termina e sai, o relocking não bloqueia o thread ou leva a um deadlock.
Então, última pergunta, por que precisamos disso?
Para função recursiva (chamada em programas multi-threaded e você deseja proteger alguns recursos / dados).
Por exemplo, você tem um programa multi thread e chama uma função recursiva na thread A. Você tem alguns dados que deseja proteger nessa função recursiva, então usa o mecanismo mutex. A execução dessa função é sequencial no thread A, então você definitivamente travaria novamente o mutex em recursão. O uso de mutex normal causa bloqueios. E mutex resursivo é inventado para resolver isso.
Veja um exemplo da resposta aceita
Quando usar mutex recursivo? .
A Wikipedia explica o mutex recursivo muito bem. Definitivamente vale a pena ler. Wikipedia: Reentrant_mutex