當多個線程同時訪問共享數據時,可能會由於線程間不斷切換導致操作系統的多次調用,從而引起訪問衝突。想要解決這個問題,我們可以爲這份共享數據加入互斥鎖。
互斥鎖的內部實現:
lock:
movb $0 %al
xchgb %al mutex //交換寄存器al和mutex中的內容 是原子的
if(al>0)
retuen 0
else
goto lock //掛起等待
unlock:
movb $1 mutex //喚醒等待mutex的線程
return 0;
下面以兩個進程同時對全局靜態變量count進行++操作爲例:
1 #include<stdio.h>
2 #include<pthread.h>
3
4 static int count = 0;
5 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;//定義鎖
6 void *pthread(void* arg)
7 {
8 int tmp = 0;
9 int i = 5000;
10 while(i--)
11 {
12 pthread_mutex_lock(&lock); //加鎖
13 tmp = count;
14 printf("this is %d thread,count is %d\n",(int)arg,tmp);
15 count = tmp +1;
16 pthread_mutex_unlock(&lock); //解鎖
17 }
18 return (void*)2;
19 }
20 int main()
21 {
22 pthread_t tid1,tid2;
23 pthread_create(&tid1,NULL,pthread,(void*)1);
24 pthread_create(&tid2,NULL,pthread,(void*)2);
25
26 void* status = NULL;
27 pthread_join(tid1,&status);
28 pthread_join(tid2,&status);
29 printf("count is %d\n",count);
30 return 0;
31 }
//未加互斥鎖
this is 1 thread,count is 5304
this is 1 thread,count is 5305
this is 1 thread,count is 5306
this is 1 thread,count is 5307
this is 1 thread,count is 5308
this is 1 thread,count is 5309
this is 1 thread,count is 5310
this is 1 thread,count is 5311
count is 5312
//加入互斥鎖
this is 1 thread,count is 9993
this is 1 thread,count is 9994
this is 1 thread,count is 9995
this is 1 thread,count is 9996
this is 1 thread,count is 9997
this is 1 thread,count is 9998
this is 1 thread,count is 9999
count is 10000
從上面程序的執行結果看:不加互斥鎖時,由於count++分兩步來執行,增加了系統的調用和線程的切換,導致結果不正確。而加入互斥鎖之後,解決了線程訪問衝突問題。
雖然加入互斥鎖解決了線程訪問衝突的問題,但也有可能會引起死鎖。
死鎖:
是指多個線程在運行過程中因爭奪資源而造成的一種僵局,當進程處於這種僵持狀態時,若無外力作用,它們都將無法向前推進。
產生死鎖的原因:
競爭資源(主要指非剝奪性資源和臨時資源):
1)競爭非剝奪性資源:非剝奪性資源是指當系統把資源分配給某個線程後,不嫩強行手繪資源,只有當它用完後自行釋放的資源。 例如現在有P1、P2兩個進程,兩個進程都需要A、B兩個資源同時存在才能繼續向前推進,而此時P1進程擁有A資源,P2進程擁有B資源。兩個進程都在等待對方釋放資源,此時就會陷入僵局,也就形成了死鎖。
2)競爭臨時資源:臨時資源是指由某一線程產生,再被其他線程用一段時間後便無用的資源。此時造成死鎖的原因是多個線程產生資源和申請資源的順序不當造成的。如有P1、P2、P3三個線程,線程P1、P2、P3分別產生S1、S2、S3資源。此時的運行順序如果是
P1: ... Request(s3);Release(s1)...
P2: ... Request(s3);Release(s1)...
P3: ... Request(s3);Release(s1)... 則會造成死鎖。
進程間推進順序非法:線程在運行過程中,請求和釋放資源的順序不當造成的死鎖。
產生死鎖的四個必要條件:
互斥條件:在一段時間內某資源只能被一個線程佔用。其他請求該資源的線程只能等待。
請求和保持資源:線程已保持了至少一個資源,又提出新的資源請求,而該資源又被其他線程佔用
不剝奪條件:已佔有的資源爲非剝奪性資源。
環路等待條件:在發生死鎖時,必須存在一個進程---資源的環形鏈。
死鎖的預防:
使產生死鎖的四個必要條件中的2 3 4 條件中的一個不能成立。
摒棄“請求和保持”條件:要求線程在申請資源時必須把所要求的資源全部申請完。
摒棄“不剝奪”條件:當一個線程已經擁有了一些資源時,又有新的資源請求,如果要申請的資源已被佔用,必須釋放已經擁有的所以資源。
摒棄“環路等待”條件:爲系統中所有資源編上編號,每個進程申請資源時都必須按照編號的順序來申請。
死鎖的避免:
只要使系統始終都處於安全狀態,便可避免死鎖的產生。
安全狀態:系統能按某種線程順序(安全序列)來爲每個線程分配其所需資源,直至滿足每個線程對資源的最大需求,使每個線程都可順利完成。
最典型的方法是:銀行家算法。