What and why a recursive mutex should not be such a complicated thing as described in the accepted answer.
I would like to write down my understanding after some digging on the net.
First, you must understand that when it comes to a mutex, multi-threaded concepts are also clearly involved. (mutex is used for synchronization. I do not need a mutex if there is only 1 thread in my program)
Secondly, you need to know the difference between a normal mutex and a recursive mutex .
Quoted from APUE:
(A recursive mutex is) A mutex type that allows the same thread to block it several times, without first unlocking it.
The main difference is that a recursive lock inside the same thread does not cause deadlock and does not block the thread.
Does this mean that recursive locking never causes a deadlock?
No, it can still cause a deadlock like a regular mutex if you blocked it in one thread without unlocking it and tried to block it in other threads.
Let's see the code as evidence.
- normal mutex with a dead end
#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); }
exit:
thread1 thread1 hey hey thread2
An example of a common dead end, no problem.
- recursive mutex with dead end
Just uncomment this line
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
and comment on another.
exit:
thread1 thread1 hey hey thread2
Yes, a recursive mutex can also cause a dead end.
- normal mutex, lock in the same topic
#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); }
exit:
thread1 func3 thread2
Deadlock in thread t1 , in func3 .
(I use sleep(2) to make it easier to see that the deadlock is primarily caused by locking in func3 )
- recursive mutex, blocking in the same thread
Again, uncomment the line of the recursive mutex and comment out the other line.
exit:
thread1 func3 func3 hey hey thread1 hey hey thread2
Deadlock in thread t2 , in func2 . See? func3 shuts down and exits; re-locking does not block the thread and does not cause deadlock.
So the last question, why do we need this?
For a recursive function (called in multi-threaded programs and you want to protect some resources / data).
For example, you have a multi-threaded program, and you call a recursive function in thread A. You have some data that you want to protect in this recursive function, so you use the mutex mechanism. The execution of this function is sequential in thread A, so you will definitely lock the mutex in recursion. Using a normal mutex causes deadlocks. And an inventive mutex is invented to solve this.
See an example from the accepted answer. When to use a recursive mutex? ,
Wikipedia explains the recursive mutex very well. Definitely worth reading. Wikipedia: Reentrant_mutex