Cái gì và tại sao của mutex đệ quy không nên là một điều phức tạp được mô tả trong câu trả lời được chấp nhận.
Tôi muốn viết ra sự hiểu biết của mình sau khi tìm hiểu kỹ trên mạng.
Đầu tiên, bạn nên nhận ra rằng khi nói về mutex , khái niệm đa luồng chắc chắn cũng có liên quan. (mutex được sử dụng để đồng bộ hóa. Tôi không cần mutex nếu tôi chỉ có 1 luồng trong chương trình của mình)
Thứ hai, bạn nên biết sự khác biệt giữa mutex bình thường và mutex đệ quy .
Trích dẫn từ APUE :
(Mutex đệ quy là a) Loại mutex cho phép cùng một luồng khóa nó nhiều lần mà không cần mở khóa trước.
Sự khác biệt chính là trong cùng một luồng , khóa lại một khóa đệ quy không dẫn đến deadlock, không chặn luồng.
Điều này có nghĩa là khóa lặp lại không bao giờ gây ra bế tắc?
Không, nó vẫn có thể gây ra deadlock như mutex bình thường nếu bạn đã khóa nó trong một chuỗi mà không mở khóa nó và cố gắng khóa nó trong các chuỗi khác.
Hãy xem một số mã làm bằng chứng.
- mutex bình thường với bế tắc
#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);
}
đầu ra:
thread1
thread1 hey hey
thread2
ví dụ về bế tắc chung, không có vấn đề gì.
- đệ quy mutex với deadlock
Chỉ cần bỏ ghi chú dòng này
error = pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
và bình luận dòng kia.
đầu ra:
thread1
thread1 hey hey
thread2
Có, mutex đệ quy cũng có thể gây ra bế tắc.
- mutex bình thường, khóa lại trong cùng một chủ đề
#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);
}
đầu ra:
thread1
func3
thread2
Chốt lại thread t1
, vào func3
.
(Tôi sử dụng sleep(2)
để làm cho nó dễ dàng hơn thấy rằng bế tắc đầu tiên là do khóa lại func3
)
- mutex đệ quy, khóa lại trong cùng một chủ đề
Một lần nữa, bỏ ghi chú dòng mutex đệ quy và nhận xét dòng còn lại.
đầu ra:
thread1
func3
func3 hey hey
thread1 hey hey
thread2
Chốt lại thread t2
, vào func2
. Xem? func3
kết thúc và thoát ra, khóa lại không chặn luồng hoặc dẫn đến bế tắc.
Vì vậy, câu hỏi cuối cùng, tại sao chúng ta cần nó?
Đối với hàm đệ quy (được gọi trong các chương trình đa luồng và bạn muốn bảo vệ một số tài nguyên / dữ liệu).
Ví dụ: Bạn có một chương trình đa luồng và gọi một hàm đệ quy trong luồng A. Bạn có một số dữ liệu mà bạn muốn bảo vệ trong hàm đệ quy đó, vì vậy bạn sử dụng cơ chế mutex. Việc thực thi hàm đó là tuần tự trong chuỗi A, vì vậy bạn chắc chắn sẽ khóa lại mutex trong đệ quy. Sử dụng mutex bình thường gây ra bế tắc. Và mutex đệ quy được phát minh để giải quyết điều này.
Xem ví dụ từ câu trả lời được chấp nhận
Khi nào sử dụng mutex đệ quy? .
Wikipedia giải thích về mutex đệ quy rất tốt. Chắc chắn giá trị để đọc. Wikipedia: Reentrant_mutex