線程基礎
線程是進程的一個執行單元,執行一段程序片段,線程共享全局變量;線程的查看可以使用命令或者文件來進行查看;命令:ps -T -p <pid> -T:表示用于開啟線程查看;top -H -p <pid> -H 用于開啟線程查看;還可以通過htop 使用F2開啟樹狀圖查看選項,或者開啟顯示自定義線程名選項;F10用于退出設置;文件:/proc/PID/task 線程的名字默認和進程相同;/proc/PID/task/comm線程名稱;
線程的相關函數
線程標識
pthread_t pthread_self(void);返回值是當前線程ID,可以使用%lu打印線程pid的值;設置線程名稱:int prctl(int option,unsigned long arg2);PR_GET_NAME用于獲得當前線程的名字,PR_SET_NAME,用于設置當前線程的名字;arg2:線程名稱最大長度為15個字節,并且應該以'\0'結尾,一共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);
}
線程創建
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)/內核線程;
?分離:pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);detachstate:PTHREAD_CREATE_DETACHED:表示分離線程;PTHREAD_CREATE_JOINABLE表示非分離線程;
?線程使用的是缺省的堆棧,線程的優先級和父進程同級別;
線程的ID指針返回值是void類型的指針函數;arg:表示的是start_rtn的形式參數;返回值0表示成功,非0表示出錯;
線程消亡
線程的消亡分為正常終止和線程取消兩種情況;線程正常終止:對于子線程來說線程處理函數return 返回一個返回值,線程可以終止,線程終止還可以調用pthread_exit(void* retval);來進行處理,retval:表示函數的返回指針,只要pthread_join中的第二個參數retval不是NULL,這個值江北傳遞給retval,如果用在線程回調函數中,將返回線程數據;
對于主線程:線程合并:int pthread_join(pthread_t tid, void **retval);tid:被等待的線程標識符,retval一個用戶定義的指針,它可以用來存儲被等待線程的返回值,返回值:0表示成功,非0表示錯誤碼;當進行線程合并時,可以由其他線程來終止,并且回收資源;
pthread_join.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){
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表示錯誤碼;但是這樣是不能被其他線程終止的,存儲在它終止時,有系統自動的回收釋放;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
線程取消:取消點取消,如果線程接收到取消信號,到達指定位置才能能取消,取消點取消分為手動:void pthread_testcancle(void);自動:通過引起阻塞的系統調用來取消;
?線程取消:發送取消信號:int pthread_cancel(pthread_t pthread),需要注意的是:發送成功并不一定意味著thread線程就會終止,發送CANCEL指令后,使用pthread_join 函數,等待指定的線程完全退出以后,在繼續執行,否則容易產生段錯誤;函數的返回值0表示成功,非0值表示失敗;
?設置當前線程的取消類型:int pthread_setcancelstate(int state,int *oldstate);state:PTHREAD_CANCEL_ENABLE:線程取消啟用;PTHREAD_CANCEL_DISABLE:表示線程取消禁用;oldstate:表示線程之前的狀態;
?設置當前線程的取消類型:int pthread_setcanceltype(int type,int *oldtype);type:PTHREAD_CANCEL_DEFFERED:表示取消點取消;PTHREAD_CANCEL_ASYNCHRONOUS:表示立即退出;oldstate:表示之前的取消類型;
?設置取消點: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);
}
}
發送信號:int pthread_kill(pthread_t tid, int sig);tid:表示線程ID,sig:信號,0表示保留信號,用于測試線程是否存在,信號為正數表示系統自定義信號或者系統自定義信號;返回值:0表示調用成功,ESRCH:表示線程不存在;EINVAL:表示信號不合法;
設置線程并發度:并發度表示線程并發的數目,int pthread_setconcurrency(int level);獲取線程并發度:int pthread_getconcurrency(void);
?void pthread_cleanup_push(void (routine)(void),void *arg);void pthread_cleanup_pop(int execute);execute通常為0;關于線程的信息可以man 7 threads;
?線程間通信使用全局變量的方式進行;
線程同步
信號量
信號量的創建: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表示成功;
?觸發:sem:信號量對象;0:表示成功;-1:表示成功;
互斥量
互斥量和信號量的區別:
?1、互斥量用于線程的互斥,信號用于線程的同步互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排他性,但是滬指無法限制資源訪問者對其資源的訪問順序,也就是訪問是無序的;同步:是指在互斥的基礎上,通過其它機制實現訪問者對資源的有序訪問,在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況 都是互斥的,少數情況是指可以允許多個訪問者同時訪問資源;
?2、互斥量值只能是0或者1,信號量值可以作為非負整數,也就是說,一個互斥量只能用于一個資源的互斥訪問,它不能解決多個資源的多線程互斥問題,信號量可以實現多個同類資源的多線程互斥和同步。當信號量為單值信號量時,也可以完成一個資源的互斥訪問;
?3、互斥量的加鎖和解鎖必須有同一個線程使用,信號量可以由一個線程釋放,另一個線程得到;
?4、信號量可以允許多個線程進入臨界區,互斥體只能允許一個線程進入臨界區;
?ATM取款,toilet;互斥量分為靜態分配互斥量,實現特點是簡單,pthread_mutex = PTHREAD_MUTEX_INITIAVIZER;動態分配互斥量:特點是可以設置更多的選項;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);
}
條件變量
線程掛起直到共享數據的某些條件得到滿足;條件變量分為靜態分配條件變量,pthread_cond_t_cond = PTHREAD_COND_INITIALIZER特點是比較簡單;動態分配靜態變量:特點是可以設置更多的選項,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:表示超時等待結束;
激活操作:
?單個激活:int pthread_cond_signal(pthread_cond_t *cond);cond:表示條件變量,返回值0表示成功,正數表示錯誤碼;
?全部激活: int pthread_cond_broadcast(pthread_cond_t *cond);cond:表示條件變量,返回值0表示成功,正數表示錯誤碼;
線程讀寫鎖
讀取鎖是共享鎖,寫入鎖是獨占鎖;
?靜態分配讀寫鎖:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER,特點是實現簡單;動態分配讀寫鎖:pthread_rwlock_init(&rwlock,NULL);pthread_rwlock_destory(&rwlock);可以用于設置更多選項
關于鎖的操作
讀取鎖:加讀取鎖: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);
線程和進程的區別
接口對比:
getpid | pthread_self | 用于獲得控制流的ID |
---|---|---|
fork | pthread_create | 創建新的控制流 |
exit | pthread_exit | 退出已有的控制流 |
waitpid | pthread_join | 等待控制流并獲得結束代碼 |
1、進程是資源分配和擁有的基本單位,線程是處理器調度的基本單位;
2、進程擁有獨立的地址空間,線程共性進程的地址空間;
3、線程上下文切換比進程上下文切換要快得多;
4、子進程崩潰不會影響其它子進程,但是任何一個線程崩潰,整個程序都會崩潰;