3 回答

TA貢獻1808條經驗 獲得超4個贊
使用重入鎖,您可以編寫一個M
對資源加鎖的方法,A
然后M
遞歸調用,或從已經持有on鎖的代碼中進行調用A
。
使用非重入鎖,您將需要2個版本的M
,一個可以鎖定的版本和一個不可以鎖定的版本,以及其他用于調用正確版本的邏輯。

TA貢獻1812條經驗 獲得超5個贊
遞歸互斥鎖的內容和原因不應是公認的答案中描述的如此復雜的事情。
我想在網上進行一些探討后寫下我的理解。
首先,您應該意識到在談論互斥時,肯定也涉及多線程概念。(互斥鎖用于同步。如果程序中只有1個線程,則不需要互斥鎖)
其次,您應該知道普通互斥鎖和遞歸互斥鎖之間的區別。
引用APUE:
(遞歸互斥鎖是a)互斥鎖類型,它允許同一線程多次鎖定它而無需先將其解鎖。
關鍵區別在于,在同一線程內,重新鎖定遞歸鎖不會導致死鎖,也不會阻塞線程。
這是否意味著后退鎖定永遠不會導致死鎖?
不,如果您將死鎖鎖定在一個線程中但未將其解鎖,然后嘗試將其鎖定在其他線程中,它仍可能導致死鎖作為普通互斥鎖。
讓我們看一些代碼作為證明。
帶有死鎖的普通互斥鎖
#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??吹剑縡unc3完成并退出后,重新鎖定不會阻塞線程或導致死鎖。
那么,最后一個問題,我們為什么需要它?
對于遞歸函數(在多線程程序中調用,并且您要保護某些資源/數據)。
例如,您有一個多線程程序,并在線程A中調用了一個遞歸函數。您在該遞歸函數中有一些要保護的數據,因此可以使用互斥鎖機制。該函數的執行在線程A中是順序執行的,因此您一定要以遞歸方式重新鎖定互斥鎖。使用普通互斥鎖會導致死鎖。并且發明了遞歸互斥體來解決這個問題。
添加回答
舉報