linux 程序和執行緒 對比
執行緒和程序
這個概念不管在什麼作業系統中都是一樣的,也是面試官比較喜歡問的,代表你對程式優化的功底,搞安卓的時候,經常被用來優化處理速度 還有資料的處理,結合handler 一起處理,解決經常出現介面死掉問題。
既然總結了,這一次充分總結下:
程序
程序組成:
- 程序控制塊PCB
- 程式段
- 資料段
程序控制塊PCB 是核心中存放的一塊PCB區域,記錄型資料結構 ,PCB 記錄了作業系統所需要的引數,用來描述程序目前情況和程序執行的全部資訊,包括
- 程序描述資訊:
- 程序識別符號,識別程序
- 使用者標識,用於資源共享和保護
- 家族關係,程序關於有父程序和子程序的資訊
- 處理機狀態資訊:包含通用暫存器、指令暫存器、程式狀態字(PSW)、使用者棧指標等
- 程序狀態 ,用作程序排程的依據
- 程序的優先順序,處理機執行程序的依據
- 程序排程需要的資料: 比如已經等待CPU的總和、程序已經執行的時間總和
- 事件: 程序被阻塞的原因
- 程序控制資訊
- 程式和資料的地址
- 程序和同步通訊機制
程序的狀態: 就緒、執行、阻塞
上面是程序的各種狀態切換
當第一初始化程序的時候,程序進入就緒狀態,等待OS 分配處理機處理程序,當程序獲取處理機處理,程序執行階段,執行的時候,一般會用來處理各種事物,比如 請求 IO 處理等,但是一旦有大量IO 處理,容易進入阻塞狀態,此時就是我們經常看見電腦卡死的狀態,但是處理完後,程序會進入就緒狀態
其實還有一種狀態,finish 結束狀態,但是隻要程序死了就直接退出了
執行緒
從上面很容易看出來,建立一個程序,因為直接和linux 核心進行處理資料,需要提供大量的引數,來保障程序的安全,高效,穩定,消耗很多系統資源,但是執行緒就不一樣,可以原理是一樣的,作為輕量級,消耗系統資源很少,而且作為開發者來說,執行緒用起來更爽,更方便,執行緒只能跑在程序中,所以,和系統核心互動的資料都交給程序了,執行緒簡單方便易用,據說程序消耗資源是執行緒的30倍…..
執行緒和處理流程和 程序差不錯 ,也是 就緒、執行、阻塞、結束四個狀態
多執行緒程式設計的API
建立執行緒
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void * (*func)(void * ),void *arg);
/*
看了上面的方法,mmp、wtf 這些詞彙從我腦海中冒出來,函式引數要不要寫的那麼複雜
只能看呼叫示例了:
pthread(&thrd1,NULL,(void*)task1,(void*)&g1);
phtread thrd1 表示建立執行緒的標識
pthread_attr_t *attr 表示執行緒屬性NULL 預設
void task1 執行緒需要執行的程式碼
int g1 表示task1的引數
*/
結束執行緒
pthread_exit(void *retval);
//retval 用來存放自殺執行緒退出狀態
等待執行緒結束
// 多個執行緒啟動後,由系統負責排程,但我們並不知道哪個會先開始和結束,只能等待
int pthread_join(pthread_t th,void **thread_return);
/*
th 等待執行緒的標識
thread_return 返回的狀態
*/
多執行緒例項:
只有同步鎖的情況,執行緒之間的同步互斥
用同步互斥原理,實現對變數shareid的操作
int shareid=0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int main(int argc, char const *argv[]) {
pthread_t thrd1,thrd2;
int ret;
ret = pthread_create(&thrd1,NULL,(void *)task1,NULL);//建立執行緒
ret = pthread_create(&thrd2,NULL,(void *)task2,NULL);
pthread_join(thrd1,NULL);//等待執行緒結束
pthread_join(thrd2,NULL);
printf("shareid = %d\n",shareid );
return 0;
}
void task1(void) {
long i,tmp;
for ( i = 0; i < 10000; i++) {
if (pthread_mutex_lock(&mutex)!=0) {//加同步鎖
perror("pthread_mutex_lock");
exit(EXIT_FAILURE);
}
tmp = shareid;
tmp = tmp+1;
shareid = tmp;
if (pthread_mutex_unlock(&mutex)!=0) {//解鎖
perror("pthread_mutex_unlock");
exit(EXIT_FAILURE);
}
}
printf("task1 finished shareid = %d\n" ,shareid);
}
void task2(void) {
long i,tmp;
for ( i = 0; i < 5000; i++) {
if (pthread_mutex_lock(&mutex)!=0) {
perror("pthread_mutex_lock");
exit(EXIT_FAILURE);
}
tmp = shareid;
tmp = tmp+1;
shareid = tmp;
if (pthread_mutex_unlock(&mutex)!=0) {
perror("pthread_mutex_unlock");
exit(EXIT_FAILURE);
}
}
printf("task2 finished shareid = %d\n" ,shareid);
}
因為 pthread 不是gcc 編譯標準庫,需要用到動態庫載入,編譯編譯命令最後 加上-lpthread
gcc pthread_create.c -o pthread_create -lpthread
debug 結果:
執行緒+同步鎖+訊號量 會有更有意思
很明顯同步鎖,有點死板,每次只能操作一個變數,不能實現多個公共資源的操作,但是加上訊號量就有意思了
semaphore API 介紹
建立訊號量
#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
/*
功能: 初始化 訊號量
返回值: 成功返回0 錯誤返回-1
sem: 指向訊號量結構的指標
pshared 不為0 訊號量在程序間共享,不然只能在本程序中的所有執行緒中共享
value 給出訊號量的初始值
*/
訊號量 P操作 -1
int sem_wait(sem_t *sem);
訊號量 V操作 +1
int sem_post(sem_t *sem);
訊號量刪除
int sem_destroy(sem_t *sem);
看了上面的API,和程序中用的API 不一樣? linux ipc 程序間通訊總結
我只能這麼理解了,程序和執行緒適用不同的訊號量API
案例如下:
# define MAXSIZE 10
int stack[MAXSIZE][2];
int size = 0;
sem_t sem;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void provide_data(void){
int i;
for (i = 0; i < MAXSIZE; i++) {
stack[i][0] = i;
stack[i][1] = i;
sem_post(&sem);// 訊號量 V操作 +1
}
}
void handle_data1(void) {
int i;
while (pthread_mutex_lock(&mutex),((i =size++)<MAXSIZE)) {
pthread_mutex_unlock(&mutex);
sem_wait(&sem);
printf("Plus : %d + %d =%d \n",stack[i][0],stack[i][1],stack[i][0]+stack[i][1] );
}
pthread_mutex_unlock(&mutex);
}
void handle_data2(void) {
int i;
while (pthread_mutex_lock(&mutex),((i =size++)<MAXSIZE)) {
pthread_mutex_unlock(&mutex);
sem_wait(&sem);
printf("Multiple : %d × %d =%d \n",stack[i][0],stack[i][1],stack[i][0]*stack[i][1] );
}
pthread_mutex_unlock(&mutex);
}
int main(int argc, char const *argv[]) {
pthread_t thd1,thd2,thd3;
sem_init(&sem,0,0);
pthread_create(&thd1,NULL,(void *)handle_data1,NULL);
pthread_create(&thd2,NULL,(void *)handle_data2,NULL);
pthread_create(&thd3,NULL,(void *)provide_data,NULL);
pthread_join(thd1,NULL);
pthread_join(thd2,NULL);
pthread_join(thd3,NULL);
sem_destroy(&sem);
return 0;
}
有一個執行緒th3對 stack 迴圈賦值,並且每賦值一次 將訊號量sem V操作,thd1和thd2這兩個執行緒會競爭獲取stack 操作 thd1 是求和,而thd2是求商,哪一個執行緒獲取資源了,求和或者求商,然後對sem程序P操作,所以每次執行程式會列印不同的結果
這裡還有一個知識點, 逗號運算子
while (pthread_mutex_lock(&mutex),((i =size++)<MAXSIZE)) {
pthread_mutex_unlock(&mutex);
/*
while 有兩個表示式,最後只判斷((i =size++) <MAXSIZE) 是否符合,前面只是對mutex 進行加鎖操作,這樣只是為了保證size 能正常執行++操作和判斷
*/
逗號表示式: 逗號前面和後面的表示式都執行,但是隻有最後一個返回值有效
其他unbelieve知識點
pthread_create 上面第二個引數一直設定NULL 系統預設屬性,但是如果我們設定,該如何設定呢?
如果設定執行緒屬性,必須在pthread_create 之前使用pthread_attr_init.
pthread_attr_t 結構體包含 是否繫結,是否分離,堆疊地址,堆疊大小,優先順序資訊
預設屬性是 非繫結 非分離 預設1M大小堆疊 優先順序和程序一樣
輕程序概念,理解為核心程序,位於使用者層和系統層之間,系統對執行緒資源的分配通過輕程序實現,如果設定繫結在輕程序,那個執行緒響應度高。
繫結狀態
設定繫結狀態的函式 pthread_attr_setscope
繫結:PTHREAD_SCOPE_SYSTEM 非繫結 PTHREAD_SCOPE_PROCESS
分離狀態
執行緒的分離狀態,用來決定如何結束自己
非分離狀態使用pthread_join 處理,才能釋放資源,分離狀態的執行緒,執行結束,自動釋放
非分離:PTHREAD_CREATE_JOINABLE 分離:PTHREAD_CREATE_DETACHED 設定引數的函式 pthread_attr_setdetachstate如果設定執行緒分離狀態,執行緒執行太快,在pthread_attr_init 之後,但是在pthread_create之前結束,那麼此時建立執行緒,會得到錯誤執行緒號,需要避免,最簡單的方法 執行pthread_cond_timewait 使得執行緒慢點跑
執行緒優先順序
執行緒優先順序 存放在結構體sched_param 用pthread_attr_getsparam 函式 和pthread_attr_setschedpara 存放, 先取得優先順序,修改後,存放回去
執行緒建立後改變屬性:
殺死其他執行緒的方法 pthread_cancel(thread)
但是執行緒可以設定屬性 拒絕被殺死
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)
執行緒真的很不錯的,優化用的特別多