線程基礎(chǔ)
線程是進程的一個執(zhí)行單元躯护,執(zhí)行一段程序片段庞钢,線程共享全局變量;線程的查看可以使用命令或者文件來進行查看;命令:ps -T -p <pid> -T:表示用于開啟線程查看室谚;top -H -p <pid> -H 用于開啟線程查看;還可以通過htop 使用F2開啟樹狀圖查看選項溉旋,或者開啟顯示自定義線程名選項救军;F10用于退出設(shè)置;文件:/proc/PID/task 線程的名字默認和進程相同毒嫡;/proc/PID/task/comm線程名稱癌蚁;
線程的相關(guān)函數(shù)
線程標識
pthread_t pthread_self(void);返回值是當前線程ID兜畸,可以使用%lu打印線程pid的值努释;設(shè)置線程名稱:int prctl(int option,unsigned long arg2);PR_GET_NAME用于獲得當前線程的名字咬摇,PR_SET_NAME伐蒂,用于設(shè)置當前線程的名字;arg2:線程名稱最大長度為15個字節(jié)肛鹏,并且應該以'\0'結(jié)尾逸邦,一共16個字符;返回值:0表示成功非0表示出錯在扰;
thread01.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
int main(){
printf("PID:%d,TID:%lu\n",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_SET_NAME,"test");
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
thread02.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
info();
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
sleep(1);
}
線程創(chuàng)建
int pthread_create(pthread_t *tidp,pthread_attr_t *attr,void (start_rtn)(void),void *arg)缕减;tidp:線程ID指針;
?attr:線程屬性:綁定:pthread_attr_setscope(pthread_attr_t *attr, int scope)芒珠;scope:PTHREAD_SCOPE_PROCESS:表示不進行綁定桥狡,這個是默認的選項;PTHREAD_SCOPE_SYSTEM表示綁定輕進程(LWP)/內(nèi)核線程;
?分離:pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)裹芝;detachstate:PTHREAD_CREATE_DETACHED:表示分離線程部逮;PTHREAD_CREATE_JOINABLE表示非分離線程;
?線程使用的是缺省的堆棧嫂易,線程的優(yōu)先級和父進程同級別兄朋;
線程的ID指針返回值是void類型的指針函數(shù);arg:表示的是start_rtn的形式參數(shù)炬搭;返回值0表示成功蜈漓,非0表示出錯;
線程消亡
線程的消亡分為正常終止和線程取消兩種情況宫盔;線程正常終止:對于子線程來說線程處理函數(shù)return 返回一個返回值融虽,線程可以終止,線程終止還可以調(diào)用pthread_exit(void* retval);來進行處理灼芭,retval:表示函數(shù)的返回指針有额,只要pthread_join中的第二個參數(shù)retval不是NULL,這個值江北傳遞給retval彼绷,如果用在線程回調(diào)函數(shù)中巍佑,將返回線程數(shù)據(jù);
對于主線程:線程合并:int pthread_join(pthread_t tid, void **retval)寄悯;tid:被等待的線程標識符萤衰,retval一個用戶定義的指針,它可以用來存儲被等待線程的返回值猜旬,返回值:0表示成功脆栋,非0表示錯誤碼;當進行線程合并時洒擦,可以由其他線程來終止椿争,并且回收資源;
pthread_join.c:線程取消函數(shù)
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
主線程終止的另一種方式是線程分離int pthread_detach(ppthread_t tid)熟嫩;tid:表示要釋放線程的標識符秦踪,返回值0表示成功,非0表示錯誤碼掸茅;但是這樣是不能被其他線程終止的椅邓,存儲在它終止時,有系統(tǒng)自動的回收釋放昧狮;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
線程取消:取消點取消景馁,如果線程接收到取消信號,到達指定位置才能能取消陵且,取消點取消分為手動:void pthread_testcancle(void)裁僧;自動:通過引起阻塞的系統(tǒng)調(diào)用來取消;
?線程取消:發(fā)送取消信號:int pthread_cancel(pthread_t pthread)慕购,需要注意的是:發(fā)送成功并不一定意味著thread線程就會終止聊疲,發(fā)送CANCEL指令后,使用pthread_join 函數(shù)沪悲,等待指定的線程完全退出以后获洲,在繼續(xù)執(zhí)行,否則容易產(chǎn)生段錯誤殿如;函數(shù)的返回值0表示成功贡珊,非0值表示失敗涉馁;
?設(shè)置當前線程的取消類型:int pthread_setcancelstate(int state门岔,int *oldstate);state:PTHREAD_CANCEL_ENABLE:線程取消啟用烤送;PTHREAD_CANCEL_DISABLE:表示線程取消禁用寒随;oldstate:表示線程之前的狀態(tài);
?設(shè)置當前線程的取消類型:int pthread_setcanceltype(int type,int *oldtype)帮坚;type:PTHREAD_CANCEL_DEFFERED:表示取消點取消妻往;PTHREAD_CANCEL_ASYNCHRONOUS:表示立即退出;oldstate:表示之前的取消類型试和;
?設(shè)置取消點:void pthread_testcanel(void)讯泣;
pthread_cancel.c
#include <stdio.h>
#include <pthread.h>
long count = 0;
void* default_func(void* arg){
for(;;){
pthread_testcancel();
count++;
}
}
void* disable_func(void* arg){
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
for(;;){
pthread_testcancel();
count++;
}
}
void* force_func(void* arg){
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
for(;;){
count++;
}
}
void* disable_force_func(void* arg){
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
for(;;){
count++;
}
}
void* watch(void* arg){
for(;;){
sleep(1);
printf("count:%d\n",count);
}
}
void* cancel(void* arg){
sleep(5);
pthread_t* ptid = arg;
printf("cancel %lu\n",ptid[0]);
pthread_cancel(ptid[0]);
}
int main(int argc,char* argv[]){
typedef void* (*Func)(void*);
Func funcs[3] = {default_func,watch,cancel};
int c;
while((c=getopt(argc,argv,"dp"))!=-1){
switch(c){
case 'd':
funcs[0] = (funcs[0] == default_func?disable_func:disable_force_func);
break;
case 'p':
funcs[0] = (funcs[0] == default_func?force_func:disable_force_func);
break;
}
}
pthread_t tids[3];
int i=0;
for(;i<3;i++){
pthread_create(&tids[i],NULL,funcs[i],&tids);
printf("create thread %lu\n",tids[i]);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
}
發(fā)送信號:int pthread_kill(pthread_t tid, int sig)阅悍;tid:表示線程ID好渠,sig:信號晦墙,0表示保留信號,用于測試線程是否存在寡痰,信號為正數(shù)表示系統(tǒng)自定義信號或者系統(tǒng)自定義信號抗楔;返回值:0表示調(diào)用成功,ESRCH:表示線程不存在拦坠;EINVAL:表示信號不合法连躏;
設(shè)置線程并發(fā)度:并發(fā)度表示線程并發(fā)的數(shù)目,int pthread_setconcurrency(int level)贞滨;獲取線程并發(fā)度:int pthread_getconcurrency(void)入热;
?void pthread_cleanup_push(void (routine)(void),void *arg)拍棕;void pthread_cleanup_pop(int execute);execute通常為0勺良;關(guān)于線程的信息可以man 7 threads绰播;
?線程間通信使用全局變量的方式進行;
線程同步
信號量
信號量的創(chuàng)建:int sem_init(sem_t *sem,int pshared,unsigned int value)尚困;sem:表示信號量對象蠢箩;pshared:表示信號量的類型,0表示線程共享事甜,<0:表示進程共享谬泌;value:用于表示初始值;返回值0表示成功逻谦,-1表示失斦剖怠;
?銷毀:int sem_destory(sem_t *sem)邦马;sem:表示信號量對象潮峦;返回值0表示成功,-1表示失斢掠ぁ忱嘹;
等待:包括三種:阻塞等待,int sem_wait(sem_t *sem)耕渴;sem:標識信號量對象拘悦;返回值0表示成功,-1表示失敵髁场础米;非阻塞等待:int sem_trywait(sem_t *sem);sem:表示信號量對象添诉;返回值0表示成功屁桑,-1表示失敗栏赴;超時等待:int sem_timedwait(sem_t *sem蘑斧,struct timespec *abstime);sem:表示信號量對象须眷;abstime:表示等待時間竖瘾;-1表示等待超時,0表示成功花颗;
?觸發(fā):sem:信號量對象捕传;0:表示成功;-1:表示成功扩劝;
互斥量
互斥量和信號量的區(qū)別:
?1庸论、互斥量用于線程的互斥职辅,信號用于線程的同步互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排他性聂示,但是滬指無法限制資源訪問者對其資源的訪問順序罐农,也就是訪問是無序的;同步:是指在互斥的基礎(chǔ)上催什,通過其它機制實現(xiàn)訪問者對資源的有序訪問,在大多數(shù)情況下宰睡,同步已經(jīng)實現(xiàn)了互斥蒲凶,特別是所有寫入資源的情況 都是互斥的,少數(shù)情況是指可以允許多個訪問者同時訪問資源拆内;
?2旋圆、互斥量值只能是0或者1,信號量值可以作為非負整數(shù)麸恍,也就是說灵巧,一個互斥量只能用于一個資源的互斥訪問,它不能解決多個資源的多線程互斥問題抹沪,信號量可以實現(xiàn)多個同類資源的多線程互斥和同步刻肄。當信號量為單值信號量時,也可以完成一個資源的互斥訪問融欧;
?3敏弃、互斥量的加鎖和解鎖必須有同一個線程使用,信號量可以由一個線程釋放噪馏,另一個線程得到麦到;
?4、信號量可以允許多個線程進入臨界區(qū)欠肾,互斥體只能允許一個線程進入臨界區(qū)瓶颠;
?ATM取款,toilet刺桃;互斥量分為靜態(tài)分配互斥量粹淋,實現(xiàn)特點是簡單,pthread_mutex = PTHREAD_MUTEX_INITIAVIZER瑟慈;動態(tài)分配互斥量:特點是可以設(shè)置更多的選項廓啊;pthread_mutex_init(&mutex,NULL)或者pthread_mutex_destory(&mutex);
?互斥量的操作:加鎖:互斥鎖int pthread_mutex_lock(pthread_t *mutex)封豪;嘗試加鎖 int pthread_mutex_trylock(pthread_t *mutex)谴轮,mutex:表示的也是互斥鎖;解鎖:int pthread_mutex_unlock(pthread_t *mutex)吹埠,mutex同樣表示的是互斥鎖第步;
mutex01.c
#include <stdio.h>
#include <pthread.h>
void* func(void* arg){
pthread_mutex_lock(arg);
printf("enter func\n");
sleep(1);
printf("do something\n");
sleep(1);
printf("level func\n");
pthread_mutex_unlock(arg);
}
int main(int argc,int argv[]){
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
pthread_t tids[3];
int i;
for(i=0;i<3;i++){
pthread_create(&tids[i],NULL,func,&mutex);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
pthread_mutex_destroy(&mutex);
}
mutex02.c
#include <stdio.h>
#include <pthread.h>
void* func(void* arg){
pthread_cleanup_push(pthread_mutex_unlock,arg);
pthread_mutex_lock(arg);
printf("enter func\n");
sleep(1);
printf("do something\n");
sleep(1);
printf("level func\n");
pthread_exit(0);
pthread_cleanup_pop(0);
}
int main(int argc,int argv[]){
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
pthread_t tids[3];
int i;
for(i=0;i<3;i++){
pthread_create(&tids[i],NULL,func,&mutex);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
pthread_mutex_destroy(&mutex);
}
條件變量
線程掛起直到共享數(shù)據(jù)的某些條件得到滿足疮装;條件變量分為靜態(tài)分配條件變量,pthread_cond_t_cond = PTHREAD_COND_INITIALIZER特點是比較簡單粘都;動態(tài)分配靜態(tài)變量:特點是可以設(shè)置更多的選項廓推,pthread_cond_init(&cond,NULL);或者使用pthread_cond_destory(&cond)翩隧;
操作:
?條件等待:int pthrad_cond_wait(pthread_cond_t *cond樊展,pthread_mutex_t *mutex);cond:表示條件變量堆生;mutex:表示互斥鎖专缠;
?計時等待:int pthread_cond_timedwait(pthread_cond_t *cond,pthread_muex_t *mutex,const struct timespec *abstime);cond:表示條件變量淑仆;mutex:表示互斥鎖涝婉;abstime:表示等待時間;返回值:ETIMEDOUT:表示超時等待結(jié)束蔗怠;
激活操作:
?單個激活:int pthread_cond_signal(pthread_cond_t *cond)墩弯;cond:表示條件變量,返回值0表示成功寞射,正數(shù)表示錯誤碼渔工;
?全部激活: int pthread_cond_broadcast(pthread_cond_t *cond);cond:表示條件變量桥温,返回值0表示成功涨缚,正數(shù)表示錯誤碼;
線程讀寫鎖
讀取鎖是共享鎖策治,寫入鎖是獨占鎖脓魏;
?靜態(tài)分配讀寫鎖:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER,特點是實現(xiàn)簡單;動態(tài)分配讀寫鎖:pthread_rwlock_init(&rwlock,NULL)通惫;pthread_rwlock_destory(&rwlock)茂翔;可以用于設(shè)置更多選項
關(guān)于鎖的操作
讀取鎖:加讀取鎖:int pthread_rwlock_rdlock(pthread_rwlock *rwlock),int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)履腋;加寫入鎖:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)珊燎;int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);解鎖操作:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)遵湖;
線程和進程的區(qū)別
接口對比:
getpid | pthread_self | 用于獲得控制流的ID |
---|---|---|
fork | pthread_create | 創(chuàng)建新的控制流 |
exit | pthread_exit | 退出已有的控制流 |
waitpid | pthread_join | 等待控制流并獲得結(jié)束代碼 |
1悔政、進程是資源分配和擁有的基本單位,線程是處理器調(diào)度的基本單位延旧;
2谋国、進程擁有獨立的地址空間,線程共性進程的地址空間迁沫;
3芦瘾、線程上下文切換比進程上下文切換要快得多捌蚊;
4、子進程崩潰不會影響其它子進程近弟,但是任何一個線程崩潰缅糟,整個程序都會崩潰;