在學習Linux系統程式設計總結了筆記,并分享出來。有問題請及時聯系部落客:Alliswell_WP,轉載請注明出處。
09-linux-day09(線程同步)
一、内容回顧
1、守護程序:運作在背景,脫離終端,周期性執行某些任務
會話:多個程序組組成一個會話,組長程序不能建立會話。
程序組:多個程序在同一個組,第一個程序預設是組長
守護程序的步驟:
(1)建立子程序,父親退出(孤兒程序)
(2)建立會話,當會長
(3)切換目錄
(4)設定掩碼
(5)關閉檔案描述符
(6)執行核心邏輯
(7)退出
nohup & 把程序放入背景運作
2、多線程:
線程是輕量級的程序,最小的執行機關,程序是最小的資源申請機關。一個程序裡可以有多個線程。
建立線程 pthread_create
回收線程 pthred_join
線程退出 pthread_exit void *retval
殺死線程 pthread_cancel 取消點
線程分離 pthread_detach,也可以通過屬性設定
pthread_attr_setdetachstate 設定屬性分離,之前需要pthread_attr_init初始化,之後需要pthread_attr_destroy銷毀
檢視線程ID:pthread_self 在程序内唯一
二、學習目标
1、熟練掌握互斥量的使用
2、說出什麼叫死鎖以及解決方案
3、熟練掌握讀寫死鎖的使用
4、熟練掌握條件變量的使用
5、了解條件變量實作的生産者消費者模型
6、了解信号量實作的生産者消費者模型
三、線程同步
1、互斥量的使用
》互斥量
兩個線程通路同一塊共享資源,如果不協調順序,容易造成資料混亂。
>加鎖 mutex
pthread_mutex_init 初始化
pthread_mutex_destroy 摧毀
pthread_mutex_lock 加鎖
pthread_mutex_unlock(pthread_mutex_t *mutex) 解鎖
》互斥量的使用步驟:
1)初始化
2)加鎖
3)執行邏輯——操作共享資料
4)解鎖
注意事項:
加鎖需要最小力度,不要一直占用臨界區。
解決昨天的問題——模拟線程共同搶占(螢幕)資源
>vi pthread_print.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5
6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
7
8 9
10 void* thr1(void *arg){
11 while(1){
12 //先上鎖
13 pthread_mutex_lock(&mutex);//加鎖:當有線程已經加鎖的時候會阻塞
14 //臨界區
15 printf("hello");//不帶換行,沒有行緩沖,輸出不出來
16 sleep(rand()%3);
17 printf("world\n");
18 //釋放鎖
19 pthread_mutex_unlock(&mutex);
20 sleep(rand()%3);
21 }
22 }
23
24 void* thr2(void *arg){
25 while(1){
26 //先上鎖
27 pthread_mutex_lock(&mutex);
28 //臨界區
29 printf("HELLO");
30 sleep(rand()%3);
31 printf("WORLD\n");
32 //釋放鎖
33 pthread_mutex_unlock(&mutex);
34 sleep(rand()%3);
35 }
36 }
37
38 int main(int argc, char *argv[])
39 {
40 //建立兩個線程,回收兩次
41 pthread_t tid[2];
42 pthread_create(&tid[0],NULL,thr1,NULL);
43 pthread_create(&tid[1],NULL,thr2,NULL);
44
45 pthread_join(tid[0],NULL);
46 pthread_join(tid[1],NULL);
47
48 return 0;
49 }
>make
>./pthread_print
》嘗試加鎖
man pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex);
測試(已經加鎖的再次嘗試加鎖結果會怎麼樣?)
>touch mutex_trylock.c
>vi mutex_trylock.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<string.h>
5
6 pthread_mutex_t mutex;
7
8 void *thr(void *arg)
9 {
10 while(1){
11 pthread_mutex_lock(&mutex);
12 printf("hello world\n");
13 sleep(30);
14 pthread_mutex_unlock(&mutex);
15 }
16 return NULL;
17 }
18
19 int main(int argc, char *argv[])
20 {
21 pthread_mutex_init(&mutex,NULL);
22 pthread_t tid;
23 pthread_create(&tid,NULL,thr,NULL);
24 sleep(1);
25 while(1){
26 int ret = pthread_mutex_trylock(&mutex);
27 if(ret > 0){
28 printf("ret = %d,errmmsg:%s\n",ret,strerror(ret));
29 }
30 sleep(1);
31 }
32
33 return 0;
34 }
>make
>./mutex_trylock
(列印完hellow world後,一直列印:ret = 16,errmsg:Device or resource busy)
可以在以下檔案檢視errno的錯誤資訊

2、死鎖
》死鎖
鎖了又鎖,自己加了一次鎖成功,又加了一次鎖。
交叉鎖(解決辦法:每個線程申請鎖的順序要一緻。如果申請到一把鎖,申請另外一把的時候申請失敗,應該釋放已經掌握的。)
注意:互斥量隻是建議鎖。
3、讀寫鎖
》讀寫鎖
與互斥量類似,但讀寫鎖允許更高的并行性。其特點為:讀共享,寫獨占,寫優先級高。
》讀寫鎖狀态:
三種狀态:
1)讀模式下加鎖狀态(讀鎖)
2)寫模式下加鎖狀态(寫鎖)
3)不加鎖狀态
》讀寫鎖特性:
1)讀寫鎖是“寫模式加鎖”時,解鎖前,所有對該鎖加鎖的線程都會被阻塞。
2)讀寫鎖是“讀模式加鎖”時,如果線程以讀模式對其加鎖會成功;如果線程以寫模式加鎖會阻塞。
3)讀寫鎖是“讀模式加鎖”時,既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程。那麼讀寫鎖會阻塞随後的讀模式鎖請求。優先滿足寫模式鎖。讀鎖、寫鎖并行阻塞,寫鎖優先級高。
讀寫鎖也叫共享-獨占鎖。當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當它以寫模式鎖住時,它是以獨占模式鎖住的。讀共享,寫獨占。
》讀寫鎖的使用場景:非常适合于對資料結構讀的次數遠大于寫的情況。
》讀寫鎖場景練習:
1)線程A加寫鎖成功,線程B請求讀鎖
B阻塞
2)線程A持有讀鎖,線程B請求寫鎖
B阻塞
3)線程A擁有讀鎖,線程B請求讀鎖
B加鎖成功
4)線程A持有讀鎖,然後線程B請求寫鎖,然後線程C請求寫鎖
BC阻塞;A釋放後,B加鎖;B釋放後,C加鎖
5)線程A持有寫鎖,然後線程B請求讀鎖,然後線程C請求寫鎖
BC阻塞;A釋放後,C加鎖;C釋放後,B加鎖
初始化:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
或
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
4、條件變量介紹和生産者和消費者模型
5、條件變量生産者消費者模型實作
6、條件變量生産者和消費者模型示範
7、信号量的概念和函數
8、信号量實作生産者和消費者分析
9、信号量實作生産者和消費者
10、檔案鎖單開程序
11、哲學家就餐模型分析