目錄
- 執行緒控制
- 執行緒終止
- 執行緒等待
- 執行緒分離
- 執行緒互斥
- 行程執行緒間的互斥相關背景概念
- 互斥量mutex
- 互斥量實作原理探究
執行緒控制
執行緒終止
- 如果需要只終止某個執行緒而不終止整個行程,可以有三種方法:
- 從執行緒函式return,這種方法對主執行緒不適用,從main函式return相當于呼叫exit,
- 執行緒可以呼叫pthread_ exit終止自己,
- 一個執行緒可以呼叫pthread_ cancel終止同一行程中的另一個執行緒,
- return退出
#include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<pthread.h>
7 using namespace std;
8 void* run(void*arg){
9 while(1){
10 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
11 sleep(1);
12
13 }
14 return (void*)10;
15 }
16 int main(){
17 pthread_t tid;
18 pthread_create(&tid,NULL,run,(void*)"pthread 1");
19 while(1){
20 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
21 sleep(10);
22 break;
23 }
24 // void*ret=NULL;
25 // pthread_join(tid,&ret);
26 // cout<<"pthtead quit codr:"<<(long long)ret<<endl;
27 return 0;
28 }

- 主執行緒運行10秒,新執行緒運行1秒,10秒后主執行緒return退出,新執行緒跟著也推出了,
- exit退出
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<pthread.h>
8 using namespace std;
9 void* run(void*arg){
10 while(1){
11 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
12 sleep(5);
13 exit(-1);
14 }
15 return (void*)10;
16 }
17 int main(){
18 pthread_t tid;
19 pthread_create(&tid,NULL,run,(void*)"pthread 1");
20 while(1){
21 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
22 sleep(1);
23
24 }
25 // void*ret=NULL;
26 // pthread_join(tid,&ret);
27 // cout<<"pthtead quit codr:"<<(long long)ret<<endl;
28 return 0;

- 新執行緒運行5秒,主執行緒運行1秒當第五秒后行程通過exit退出,
3.pthread_exit函式
功能:執行緒終止 原型 void pthread_exit(void *value_ptr); 引數
value_ptr:value_ptr不要指向一個區域變數, 回傳值:無回傳值,跟行程一樣,執行緒結束的時候無法回傳到它的呼叫者(自身)
#include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<pthread.h>
8 using namespace std;
9 void* run(void*arg){
10 //while(1){
11 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
12 sleep(5);
13 pthread_exit((void*)10);
14 // }
15 // return (void*)10;
16 }
17 int main(){
18 pthread_t tid;
19 pthread_create(&tid,NULL,run,(void*)"pthread 1");
20 // while(1){
21 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
22 sleep(1);
23
24 // }
25 void*ret=NULL;
26 pthread_join(tid,&ret);
27 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
28 return 0;

- 新執行緒呼叫pthread_exit()終止自己,同時回傳自己的退出碼,
4.pthread_cancel函式
功能:取消一個執行中的執行緒 原型 int pthread_cancel(pthread_t thread); 引數 thread:執行緒ID
回傳值:成功回傳0;失敗回傳錯誤碼
兩種取消方式:
1.別人取消 2,自己取消自己
1,別人取消
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<pthread.h>
8 using namespace std;
9 void* run(void*arg){
10 while(1){
11 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
12 sleep(2);
13 // pthread_exit((void*)10);
14 }
15 return (void*)10;
16 }
17 int main(){
18 pthread_t tid;
19 pthread_create(&tid,NULL,run,(void*)"pthread 1");
20 // while(1){
21 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
22 sleep(10);
23
24 // }
25 pthread_cancel(tid);
26 cout<<"new pthread"<<tid<<endl;
27 void*ret=NULL;
28 pthread_join(tid,&ret);
29 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
30 return 0;
31 }

- 在主執行緒中呼叫pthread_cancal()殺死新行程,同時獲取它的退出碼 -1,
執行緒等待
- 執行緒為什么要等待?
- 已經退出的執行緒,其空間沒有被釋放,仍然在行程的地址空間內,
- 創建新的執行緒不會復用剛才退出執行緒的地址空間,
功能:等待執行緒結束
原型
int pthread_join(pthread_t thread, void **value_ptr);
引數
thread:執行緒ID
value_ptr:它指向一個指標,后者指向執行緒的回傳值
回傳值:成功回傳0;失敗回傳錯誤碼
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<pthread.h>
7 using namespace std;
8 void* run(void*arg){
9 while(1){
10 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
11 sleep(5);
12 break;
13 }
14 return (void*)10;
15 }
16 int main(){
17 pthread_t tid;
18 pthread_create(&tid,NULL,run,(void*)"pthread 1");
19 // while(1){
20 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
21 // break;
22 // }
23 void*ret=NULL;
24 pthread_join(tid,&ret);
25 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
26 return 0;
27 }

- 當主執行緒運行完時,新執行緒還沒有執行完主執行緒就會等待新執行緒,當新執行緒執行完后會回傳他的退出碼,主執行緒會捕獲新執行緒的退出碼,
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<pthread.h>
7 using namespace std;
8 void* run(void*arg){
9 while(1){
10 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
11 sleep(5);
12 int a=0;
13 a=5/a;
14
15 break;
16 }
17 return (void*)10;
18 }
19 int main(){
20 pthread_t tid;
21 pthread_create(&tid,NULL,run,(void*)"pthread 1");
22 // while(1){
23 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
24 // break;
25 // }
26 void*ret=NULL;
27 pthread_join(tid,&ret);
28 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
29 return 0;
30 }

-
當新執行緒例外時,整個行程就會出現例外,同時主執行緒也獲取不到新執行緒的退出碼,
- 呼叫該函式的執行緒將掛起等待,直到id為thread的執行緒終止,thread執行緒以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
- 如果thread執行緒通過return回傳,value_ ptr所指向的單元里存放的是thread執行緒函式的回傳值,
- 如果thread執行緒被別的執行緒呼叫pthread_ cancel例外終掉,value_ ptr所指向的單元里存放的是常數 PTHREAD_ CANCELED,
- 如果thread執行緒是自己呼叫pthread_exit終止的-value_ptr所指向的單元存放的是傳給pthread_exit的參 數,
- 如果對thread執行緒的終止狀態不感興趣,可以傳NULL給value_ ptr引數,
執行緒分離
- 默認情況下,新創建的執行緒是joinable的,執行緒退出后,需要對其進行pthread_join操作,否則無法釋放資源,從而造成系統泄漏,
- 如果不關心執行緒的回傳值,join是一種負擔,這個時候,我們可以告訴系統,當執行緒退出時,自動釋放執行緒資源,
int pthread_detach(pthread_t thread);
可以是執行緒組內其他執行緒對目標執行緒進行分離,也可以是執行緒自己分離:
pthread_detach(pthread_self());
#include<pthread.h>
8 using namespace std;
9 void* run(void*arg){
10 pthread_detach(pthread_self());
11 while(1){
12 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
13 sleep(1);
14 break;
15
16 // pthread_exit((void*)10);
17 }
18 return (void*)10;
19 }
20 int main(){
21 pthread_t tid;
22 pthread_create(&tid,NULL,run,(void*)"pthread 1");
23 // while(1){
24 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
25 sleep(2);
26
27 // }
28 //pthread_cancel(tid);
29 //cout<<"new pthread"<<tid<<endl;
30 void*ret=NULL;
31 pthread_join(tid,&ret);
32 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
33 return 0;
34 }

- 主執行緒沒有拿到執行緒的退出碼,執行緒退出時,資源自動釋放
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<pthread.h>
8 using namespace std;
9 void* run(void*arg){
10 pthread_detach(pthread_self());
11 while(1){
12 cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
13 sleep(1);
14 int a=0;
15 a=5/a;
16 break;
17
18 // pthread_exit((void*)10);
19 }
20 return (void*)10;
21 }
22 int main(){
23 pthread_t tid;
24 pthread_create(&tid,NULL,run,(void*)"pthread 1");
25 // while(1){
26 cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
27 sleep(2);
28
29 // }
30 //pthread_cancel(tid);
31 //cout<<"new pthread"<<tid<<endl;
32 void*ret=NULL;
33 pthread_join(tid,&ret);
34 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
35 return 0;
36 }
~
~

- 雖然執行緒分離 了但是只要執行緒出現例外,行程就會例外退出
執行緒互斥
行程執行緒間的互斥相關背景概念
臨界資源:多執行緒執行流共享的資源就叫做臨界資源 臨界區:每個執行緒內部,訪問臨界資源的代碼,就叫做臨界區
互斥:任何時刻,互斥保證有且只有一個執行流進入臨界區,訪問臨界資源,通常對臨界資源起保護作用
原子性(后面討論如何實作):不會被任何調度機制打斷的操作,該操作只有兩態,要么完成,要么未完 成
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<stdlib.h>
6 #include<unistd.h>
7 #include<pthread.h>
8 using namespace std;
9 int a=10;
10 void* run(void*arg){
11 while(1){
12 cout<<(char*)arg<<","<<pthread_self()<<",,,pid"<<getpid()<<endl;
13 cout<<(char*)arg<<",a:"<<a<<",address"<<&a<<endl;
14 sleep(1);
15 }
16 return (void*)10;
17 }
18 int main(){
19 pthread_t tid;
20 pthread_t pidd;
21 pthread_create(&tid,NULL,run,(void*)"pthread 1");
22 pthread_create(&pidd,NULL,run,(void*)"pthread 2");
23 sleep(10);
24 a=100;
25 // while(1){
26 cout<<"main:"<<pthread_self()<<",pid:"<<getpid()<<endl;
27 cout<<"main:a:"<<a<<",address:"<<&a<<endl;
28 sleep(2);
29
30 // }
31 //pthread_cancel(tid);
32 //cout<<"new pthread"<<tid<<endl;
33 void*ret=NULL;
34 pthread_join(tid,&ret);
35 cout<<"pthtead quit codr:"<<(long long)ret<<endl;
36 return 0;
37 }
~
~

- 這里a(被所有執行緒訪問)屬于臨界資源,訪問臨界資源的叫做臨界區,
- 三個執行緒共享a,當10秒鐘后主執行緒改掉為100后兩個新執行緒也a的值也受到影響,
互斥量mutex
- 大部分情況,執行緒使用的資料都是區域變數,變數的地址空間在執行緒堆疊空間內,這種情況,變數歸屬單個 執行緒,其他執行緒無法獲得這種變數,
- 但有時候,很多變數都需要在執行緒間共享,這樣的變數稱為共享變數,可以通過資料的共享,完成執行緒之 間的互動,
- 多個執行緒并發的操作共享變數,會帶來一些問題,
之前的售票系統會出現票量為負數的情況,為什么呢?
-
if 陳述句判斷條件為真以后,代碼可以并發的切換到其他執行緒
-
usleep 這個模擬漫長業務的程序,在這個漫長的業務程序中,可能有很多個執行緒會進入該代碼段
-
–ticket 操作本身就不是一個原子操作
要解決以上問題,需要做到三點: -
代碼必須要有互斥行為:當代碼進入臨界區執行時,不允許其他執行緒進入該臨界區,
-
如果多個執行緒同時要求執行臨界區的代碼,并且臨界區沒有執行緒在執行,那么只能允許一個執行緒進入該臨
界區, -
如果執行緒不在臨界區中執行,那么該執行緒不能阻止其他執行緒進入臨界區,
要做到這三點,本質上就是需要一把鎖,Linux上提供的這把鎖叫互斥量,

- 互斥量的介面
初始化互斥量
初始化互斥量有兩種方法:
- 互斥量的介面
-
方法1,靜態分配:
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER
- 方法2,動態分配:
>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);
引數: mutex:要初始化的互斥量 attr:NULL
銷毀互斥量
銷毀互斥量需要注意:
使用PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要銷毀
不要銷毀一個已經加鎖的互斥量
已經銷毀的互斥量,要確保后面不會有執行緒再嘗試加鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量加鎖和解鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
回傳值:成功回傳0,失敗回傳錯誤號
呼叫pthread_ lock 時,可能會遇到以下情況:
互斥量處于未鎖狀態,該函式會將互斥量鎖定,同時回傳成功
發起函式呼叫時,其他執行緒已經鎖定互斥量,或者存在其他執行緒同時申請互斥量,但沒有競爭到互斥量, 那么pthread_
lock呼叫會陷入阻塞(執行流被掛起),等待互斥量解鎖,
改進的售票系統
1: tick.cpp ? ?? buffers
1 #include<iostream>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<pthread.h>
5 using namespace std;
6 int ticket=10000;
7 pthread_mutex_t lock;
8 void *run(void *aig){
9 //int num=(int)aig;
10 usleep(1000);
11 while(1){
12 pthread_mutex_lock(&lock);
13 // usleep(1000);
14 if(ticket>0){
15 usleep(1000);
16 cout<<"pthread "<<(char*)aig<<",ticket:"<<ticket<<endl;
17 ticket--;
18 pthread_mutex_unlock(&lock);
19
20 }else{
21 pthread_mutex_unlock(&lock);
22 break;
23 }
24 }
25
26
W> 27 }
28 int main(){
29 pthread_t tid[4];
W> 30 char*tic[4]={"1","2","3","4"};
31 // pthread_t ti,d1;
32 //.autorelabel pthread_t tid2;
33 pthread_mutex_init(&lock,NULL);
34 int i=0;
35 for(;i<4;i++){
36 pthread_create(tid+i,NULL,run,(void*)tic[i]);
37
38 }
39 for(i=0;i<4;i++){
40 pthread_join(tid[i],NULL);
41 }
42 pthread_mutex_destroy(&lock);
43 }
~
~
~
~
互斥量實作原理探究
- 經過上面的例子,大家已經意識到單純的i++ 或者++i 都不是原子的,有可能會有資料一致性問題
- 為了實作互斥鎖操作,大多數體系結構都提供了swap或exchange指令,該指令的作用是把暫存器和記憶體單元的資料相交換,由于只有一條指令,保證了原子性,即使是多處理器平臺,訪問記憶體的 總線周期也有先后,一個處理器上的交換指令執行時另一個處理器的交換指令只能等待總線周期, 現在我們把lock和unlock的偽代碼改一下

互斥鎖底層簡單介紹:
執行緒什么時間都可能換出,這里只介紹了其中一種,
以執行緒1為主執行緒介紹,當執行緒1進入時執行movb后把0放到%al暫存器中,接著執行xchgb陳述句(而mutex記憶體區默認從1開始),把%al和mutex的值換了,當執行完后,執行緒1杯切走,要進行背景關系保護,把%al暫存器中的值放到特定暫存器中,而mutex的值不變還為0,接著當執行緒2進入時執行movb后把0放到%al暫存器中,接著執行xchgb陳述句把%al和mutex的值換了,當執行if因為%alzhong為0不滿足而執行else陳述句后進入掛起狀態,接著執行緒1切回從剛才切出的地方重新執行出現把特定暫存器的值放入%al暫存器中(恢復現場),在執行if后條件滿足鎖申請成功

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/282264.html
標籤:其他
