執行緒學習知識總結
一.執行緒的概念
在一個程式裡的一個執行路線就叫做執行緒.
一切程式至少都有一個執行執行緒
執行緒在程式內部執行,本質是在程式地址空間內執行
在Linux系統中,在CPU眼裡,看到的pcb都要比傳統的程式更加輕量化
透過程式虛擬地址空間,可以看到程式的大部分資源,將程式資源合理分配給每個執行流,就形成了執行緒執行流
執行緒的優點:
- 建立一個新執行緒的代價要比建立一個新程式小的多
- 與程式之間的切換相比,執行緒之間的切換需要作業系統做的工作要少很多
- 執行緒佔用的資源要比程式少很多
- 能充分利用多處理器的可並行數量
- 在等待慢速I/O操作結束的同時,程式可執行其他計算任務
- 計算密集型應用,為了能在多處理器系統上執行,將計算分解到多個執行緒中實現
- I/O密集型應用,為了提高效能,將I/O操作重疊,執行緒可以同時等待不同的I/O操作
有關執行緒的函式:
//標頭檔案:
#include <pthread.h>
函式體:
int pthread_create(pthread_t *thread, //執行緒ID的地址
const pthread_attr_t *attr, //填NULL
void *(*start_routine) (void *),//執行緒處理函式
void *arg); //函式引數(填NULL)
int pthread_detach(pthread_t thread);//執行緒分離
void pthread_exit(void *retval);//執行緒退出
int pthread_join(pthread_t thread, //執行緒id
void **retval); //填NULL
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/syscall.h>
void* route(void* args){
while(1){
printf("xiancheng %u %d\n",pthread_self(),syscall(SYS_gettid));
}
}
int main(void){
pthread_t pid;
pthread_create(&pid,NULL,route,NULL); //建立執行緒
while(1){
printf("main return %u %d\n",pthread_self(),syscall(SYS_gettid));
}
pthread_join(pid,NULL); //回收執行緒
}
二.有關互斥量的函式
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥量的定義
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);//互斥量初始化
int pthread_mutex_lock(pthread_mutex_t *mutex);//互斥量上鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);//互斥量解鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);//釋放互斥量
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int a;
int b;
pthread_mutex_t mutex; //定義互斥量
void* r1(void* arg){
while(1){
pthread_mutex_lock(&mutex);
a++;
b++;
if(a != b){
printf("%d!=%d\n",a,b);
a = 0;
b = 0;
}
pthread_mutex_unlock(&mutex);
}
}
void* r2(void* arg){
while(1){
pthread_mutex_lock(&mutex);
a++;
b++;
if(a != b){
printf("%d!=%d\n",a,b);
a = 0;
b = 0;
}
pthread_mutex_unlock(&mutex);
}
}
int main(void){
//建立兩個執行緒
pthread_t t1,t2;
pthread_create(&t1,NULL,r1,NULL);
pthread_create(&t2,NULL,r2,NULL);
//初始化互斥量
pthread_mutex_init(&mutex,NULL);
//回收兩個執行緒
pthread_join(t1,NULL);
pthread_join(t2,NULL);
//釋放互斥量
pthread_mutex_destroy(&mutex);
}
main函式中用pthread_exit退出:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* route(void* arg){
while(1){
printf(".");
fflush(stdout);
sleep(1);
}
}
int main(void){
pthread_t tid;
pthread_create(&tid,NULL,route,NULL);
pthread_detach(tid); //執行緒分離
pthread_exit(NULL);
}
在main執行緒中呼叫pthread_exit
會起到只讓main執行緒退出,但是保留程式資源,供其他由main建立的執行緒使用,直至所有執行緒都結束,此時程式變為殭屍程式,可以被kill殺死,但在其他執行緒中不會有這種效果.
多執行緒互斥量的使用:
賣票問題:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int g_ticket = 100;
pthread_mutex_t mutex;
void* route(void* arg){
char* p = (char*)arg;
while(1){
pthread_mutex_lock(&mutex);
if(g_ticket > 0){
printf("%s賣%d\n",p,g_ticket);
g_ticket--;
}else{
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
usleep(10000);
}
}
int main(void){
pthread_t t1,t2,t3,t4;
pthread_create(&t1,NULL,route,"thread1");
pthread_create(&t2,NULL,route,"thread2");
pthread_create(&t3,NULL,route,"thread3");
pthread_create(&t4,NULL,route,"thread4");
pthread_mutex_init(&mutex,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
pthread_join(t4,NULL);
pthread_mutex_destroy(&mutex);
}
++問題:++操作並不屬於原子操作,多執行緒執行時需要對++操作進行互斥訪問
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int g_data = 0;
pthread_mutex_t mutex;
void* route(void* arg){
int i;
for(i = 0;i < 100000;i++){
pthread_mutex_lock(&mutex);
g_data++;
pthread_mutex_unlock(&mutex);
}
}
int main(void){
pthread_t t1,t2,t3;
pthread_create(&t1,NULL,route,NULL);
pthread_create(&t2,NULL,route,NULL);
pthread_create(&t3,NULL,route,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
pthread_mutex_destroy(&mutex);
printf("%d\n",g_data);
}
- 執行緒清理函式(pthread_cleanup_push和pthread_cleanup_pop):
一般來說,POSIX的執行緒終止有兩種情況:正常終止和非正常終止.執行緒主動呼叫pthread_exit()或者從執行緒函式中return都將使執行緒正常退出,這是可預見的退出方式;非正常終止是執行緒在其他執行緒的干預下,或者由於自身執行出錯(比如訪問非法地址)而退出,這種退出方式是不可預見的.
不論是可預見的執行緒終止還是異常終止,都存在資源釋放的問題,在不考慮因執行出錯而退出的前提下,如何保證執行緒終止時能順利的釋放自己所佔用的資源,特別是鎖資源,就是一個必須考慮解決的問題.
最經常出現的情形是資源獨佔鎖的使用:執行緒為了訪問臨界資源而為其加上鎖,但在訪問過程中被外界取消,如果執行緒處於響應取消狀態,且採用非同步方式響應,或者在開啟獨佔鎖以前的執行路徑上存在取消點,則該臨界資源將永遠處於鎖定狀態得不到釋放。外界取消操作是不可預見的,因此的確需要一個機制來簡化用於資源釋放的程式設計。
“執行緒取消函式”即執行緒被取消或者下面描述的情況發生時自動呼叫的函式。它一般用於釋放一些資源,比如釋放鎖,以免其它的執行緒永遠 也不能獲得鎖,而造成死鎖。
pthread_cleanup_push()函式執行壓棧清理函式的操作,而pthread_cleanup_pop()函式執行從棧中刪除清理函式的操作。
在下面三種情況下,pthread_cleanup_push()壓棧的“清理函式”會被呼叫:
- 執行緒呼叫pthread_exit()函式,而不是直接return.
- 響應取消請求時,也就是有其它的執行緒對該執行緒呼叫pthread_cancel()函式。
- 本執行緒呼叫pthread_cleanup_pop()函式,並且其引數非0
注意:
1.當pthread_cleanup_pop()函式的引數為0時,僅僅線上程呼叫pthread_exit函式或者其它執行緒對本執行緒呼叫
pthread_cancel函式時,才在彈出“清理函式”的同時執行該“清理函式”。
2.注意pthread_exit終止執行緒與執行緒直接return終止執行緒的區別,呼叫return函式是不會在彈出“清理函式”的同時執行該“清理函式的。
3 .pthread_cleanup_push()函式與pthread_cleanup_pop()函式必須成對的出現在同一個函式中。
4.線上程宿主函式中主動呼叫return,如果return語句包含在pthread_cleanup_push()/pthread_cleanup_pop()對中,則不會引起清理函式的執行,反而會導致segment fault。
push進去的函式可能在以下三個時機執行:
- 顯示的呼叫pthread_exit();
- 在cancel點執行緒被cancel。
- pthread_cleanup_pop()的引數不為0時。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;
void clean(void* arg){
pthread_mutex_unlock(&mutex);
}
void* r1(void* arg){
int i;
for(i = 1; ;i+=2){
pthread_cleanup_push(clean,NULL);
pthread_mutex_lock(&mutex);
printf("odd-->%d\n",i);
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
}
}
void* r2(void* arg){
int i;
for(i = 0; ;i+=2){
pthread_mutex_lock(&mutex);
printf("even==>%d\n",i);
pthread_mutex_unlock(&mutex);
}
}
int main(void){
pthread_t t1,t2;
pthread_create(&t1,NULL,r1,NULL);
pthread_create(&t2,NULL,r2,NULL);
pthread_mutex_init(&mutex,NULL);
sleep(4);
pthread_cancel(t1);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
}
讀寫鎖:
- 讀讀共享
- 讀寫互斥
- 寫寫互斥
注意:寫獨佔,讀共享,寫鎖優先順序高
#include <pthread.h>
pthread_rwlock_t; //讀寫鎖定義
pthread_rwlock_init;//讀寫鎖初始化
pthread_rwlock_rdlock;//讀上鎖
pthread_rwlock_wrlock;//寫上鎖
pthread_rwlock_unlock;//解鎖
pthread_rwlock_destroy;//回收讀寫鎖
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_rwlock_t lock;
int g_data = 1;
void* route_read(void* arg){
while(1){
pthread_rwlock_rdlock(&lock);
printf("threadid = %d g_data=%d\n",pthread_self(),g_data);
//sleep(1); //讀鎖迴圈進入,寫鎖餓死,g_data的值一直列印1
pthread_rwlock_unlock(&lock);
sleep(1);//讀鎖不會無縫迴圈,寫鎖可以進入
}
}
void* route_write(void* arg){
while(1){
pthread_rwlock_wrlock(&lock);
g_data++;
pthread_rwlock_unlock(&lock);
}
}
int main(void){
pthread_t t1,t2,t3;
pthread_create(&t1,NULL,route_read,NULL);
pthread_create(&t2,NULL,route_read,NULL);
pthread_create(&t3,NULL,route_write,NULL);
pthread_rwlock_init(&lock,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
pthread_rwlock_destroy(&lock);
}
正在讀鎖上,來了讀鎖,可以進來,如果來了寫鎖2,當前寫鎖及其以後的讀鎖都阻塞,並且寫鎖優先.
條件變數:
當一個執行緒互斥的訪問某個變數時,它可能發現在其它執行緒改變狀態之前,它什麼也做不了
例如一個執行緒訪問佇列時,發現佇列為空,它只能等待,直到其它執行緒將一個節點新增到佇列中.這種情況就需要用到條件變數.
同步概念與競態條件:
同步:在保證資料安全的前提下,讓執行緒能夠按照某種特定的順序訪問臨界資源,從而有效避免飢餓問題,叫做同步.
競態條件:因為時序問題,而導致程式異常,我們稱之為競態條件.
#include <pthread.h>
pthread_cond_t;//定義條件變數
pthread_cond_init;//條件變數初始化
pthread_cond_wait;//等待條件滿足
pthread_cond_signal;//喚醒等待
pthread_cond_destroy;//條件變數的銷燬
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
void* r1(void* arg){
while(1){
pthread_cond_wait(&cond,&mutex); //阻塞等待
printf("活動開始了\n");
}
}
void* r2(void* arg){
while(1){
sleep(1);
pthread_cond_signal(&cond); //喚醒等待
}
}
int main(void){
pthread_t t1,t2;
pthread_create(&t1,NULL,r1,NULL);
pthread_create(&t2,NULL,r2,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
相關文章
- 建立執行緒知識點總結執行緒
- 多執行緒(三)、執行緒池 ThreadPoolExecutor 知識點總結執行緒thread
- Java 多執行緒 | 併發知識問答總結Java執行緒
- 多執行緒基礎必要知識點!看了學習多執行緒事半功倍執行緒
- JavaEE進階知識學習----多執行緒JUC高階知識-5-執行緒池-Callable-執行緒排程Java執行緒
- 最全java多執行緒學習總結1--執行緒基礎Java執行緒
- Java多執行緒學習(五)執行緒間通訊知識點補充Java執行緒
- C++知識點:對於多執行緒的總結C++執行緒
- 《面試補習》- 多執行緒知識梳理面試執行緒
- Vue學習知識點總結Vue
- 清明花了幾天總結了多執行緒的知識點執行緒
- Java常見知識點彙總(⑬)——執行緒Java執行緒
- Java多執行緒學習(吐血超詳細總結)Java執行緒
- 40個關於Java多執行緒知識點問題總結Java執行緒
- 第十週學習知識總結
- PG知識點學習總結圖
- 【java學習】java知識點總結Java
- 執行緒基本知識點執行緒
- Java常見知識點彙總(⑭)——執行緒池Java執行緒
- redis執行緒模型-學習小結Redis執行緒模型
- 多執行緒程式設計總結:一、認識多執行緒本質執行緒程式設計
- Redis基礎知識(學習筆記6--執行緒IO模型)Redis筆記執行緒模型
- Java執行緒總結Java執行緒
- 【Go學習】Go(Golang)知識點總結Golang
- Redis Cluster叢集知識學習總結Redis
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 多執行緒面試必備基礎知識彙總執行緒面試
- 前置知識—程式和執行緒執行緒
- 多執行緒基礎知識執行緒
- Java多執行緒程式設計基礎知識彙總Java執行緒程式設計
- JAVA學習-------第二週知識點總結Java
- Java基礎知識學習筆記總結Java筆記
- EXTJs學習筆記(知識點總結)JS筆記
- 多執行緒:執行緒池理解和使用總結執行緒
- iOS 多執行緒總結iOS執行緒
- java多執行緒總結Java執行緒
- 執行緒併發總結執行緒
- Java多執行緒相關知識Java執行緒