您好,登錄后才能下訂單哦!
臨界資源:
一個進程的資源對于運行在它內部的線程是共享的,一次只允許一個線程使用的資源叫做臨界資源
臨界區:
訪問臨界資源的那段程序叫做臨界區
線程的同步:
同步就是協同步調,按照預定的先后順序執行。
“同”字應是指協同、協助、互相配合。
線程的互斥:
某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。
但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
互斥量(mutex)
多個線程同時訪問共享數據時,經常會發生沖突,因此常常引入互斥鎖來解決這個問題。
沖突:例如對一個全局變量g_var進行加一操作,這樣的操作需要三條指令來完成:(1、把該變量的值從內存讀到寄存器;2、寄存器進行加一操作;3、把修改后的值寫回內存),因為這三條指令的執行不是原子操作,有可能線程1執行了第一步(把變量值讀到寄存器),此時內核調度線程2,線程2也把該值讀到寄存器,它們都進行加一操作后寫回內存,導致最后結果只加了一次,而我們希望是兩次。
解決:引入互斥鎖,獲得鎖的線程可以對變量進行“讀-修改-寫”操作,這個操作是原子的,完成這個操作后釋放鎖給別的線程,沒有獲得鎖的線程只能等待而不能訪問該共享數據。
有關函數:
1.互斥鎖的初始化及銷毀
其中初始化既可以使用函數pthread_mutex_init(),也可以定義pthread_mutex_t類型的變量mutex,并賦值PTHREAD_MUTEX_INITIALIZER
2.對mutex的加鎖及解鎖
一個線程可以調用pthread_mutex_lock獲得Mutex,如果這時另一個線程已經調用pthread_mutex_lock獲得了該Mutex,則當前線程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋放Mutex,當前線程被喚醒,才能獲得該Mutex并繼續執行。
如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。
Mutex的lock和unlock的偽碼(以x86的xchg指令為例):
不使用互斥量代碼:
#include <stdio.h> #include <pthread.h> void *thread(void *arg) { int count=2000; while(count-- >0){ int var=g_var; printf("this is thread%d,g_var=%d\n",(int)arg,g_var); g_var=var+1; } } int main() { pthread_t tid1,tid2; if(pthread_create(&tid1,NULL,thread,(void*)1)!=0){ printf("creat tid1 is failed\n"); } if(pthread_create(&tid1,NULL,thread,(void*)2)!=0){ printf("creat tid1 is failed\n"); } pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
運行結果:
第一次運行線程1和線程2一共加了2275次
第二次運行一共把g_var加了2000次
使用互斥量實現代碼:
include <stdio.h> #include <pthread.h> //pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER; struct info { int _var; pthread_mutex_t *_mutex_lock; }info; int g_var=0; void *thread(void *arg) { struct info* _lock=(struct info*)arg; int count=2000; while(count-- >0){ pthread_mutex_lock(_lock->_mutex_lock); int var=g_var; printf("this is thread%d,g_var=%d\n",_lock->_var,g_var); g_var=var+1; pthread_mutex_unlock(_lock->_mutex_lock); } } int main() { pthread_t tid1,tid2; pthread_mutex_t mutex_lock; int err=pthread_mutex_init(&mutex_lock,NULL); if(err!=0){ printf("%s\n",strerror(err)); } struct info info1; info1._var=1; info1._mutex_lock=&mutex_lock; if(pthread_create(&tid1,NULL,thread,&info1)!=0){ printf("creat tid1 is failed\n"); } struct info info2; info2._var=2; info2._mutex_lock=&mutex_lock; if(pthread_create(&tid2,NULL,thread,&info2)!=0){ printf("creat tid2 is failed\n"); } pthread_join(tid1,NULL); pthread_join(tid2,NULL); err=pthread_mutex_destroy(&mutex_lock); if(err!=0){ printf("%s\n",strerror(err)); } return 0; }
運行結果:
線程1和線程2共執行了4000次
總結:當一個線程已經獲得鎖,另一個線程需掛起等待,當該線程釋放鎖后,會喚醒正在等待線程。每個mutex都有一個等待隊列,當線程掛起時,就加入該等待隊列,狀態設置為睡眠。一個線程要喚醒別的線程,只需要從等待隊列中取出一項加入就緒隊列并把它的狀態設置為就緒。
有的時候一個線程會兩次獲得鎖,第一次正常獲得,第二次獲得所得時候發現鎖已經被別占用,它就會掛起,導致自己占用了鎖卻處于掛起狀態,這樣就產生了死鎖。
死鎖:
產生死鎖的原因
1、進程資源不足
2、進程推進的順序不合適
3、系統資源的分配不當
產生死鎖的必要條件:
1、互斥條件:一個資源一次只能被一個進程使用
2、請求與保持條件:一個已經進程因請求資源而阻塞,對已獲得的資源保持不放
3、不剝奪條件:進程已獲得的資源,在沒使用完之前,不能強行剝奪
4、循環等待條件:若干個進程之間形成一種頭尾相接的循環等待資源關系
產生死鎖時,以上四個必要條件一定都成立。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。