一、多線程 VS 多進程
和進程相比,線程有很多優勢。在Linux系統下,啓動一個新的進程必須分配給它獨立的地址空間,建立衆多的數據表來維護代碼段和數據。而運行於一個進程中的多個線程,他們之間使用相同的地址空間。正是這樣,同一進程下的線程之間共享數據空間,數據可以相互使用,並且線程間切換也要更快些,可以更有效的利用CPU。
二、程序設計
[注] 頭文件<pthread.h> 編譯時要加載動態庫 libpthread.a,使用 -lpthread
1、創建線程
2、等待線程
3、關閉線程
4、退出清除
1、創建線程
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, void *(*start_rtn)(void), void *arg)
tidp爲線程id,是函數分配的值,所以要傳一個 pthread_t 的地址。
attr線程屬性,通常爲空。
start_rtn爲線程要執行的函數,返回值爲空指針,參數爲後面的*arg
若成功則返回0,否則返回出錯編號。
例:
- #include<stdio.h>
- #include<pthread.h>
- void *func1(void *arg){ //原函數聲明
- int i;
- for(i=0;i<5;i++){
- printf("this is func1! The num is %d\n",*(int*)arg); //將空指針轉換爲int型指針
- sleep(1);
- }
- }
- void *func2(int *m){ //自定義類型聲明,也可以定義非指針類型,但是在create時會有警告,因爲非地址並不能改變傳入的值
- int i;
- for(i=0;i<5;i++){
- printf("this is func2! The num is %d\n",*m);
- (*m)++;
- sleep(1);
- }
- }
- int main(){
- pthread_t id1,id2;
- int num = 5;
- int *p = #
- if(pthread_create(&id1,NULL,(void *)func1,(void *)p) != 0){
- printf("thread1 create error!\n");
- return -1;
- }
- if(pthread_create(&id2,NULL,(void *)func2,&num) != 0){
- printf("thread2 create error!\n");
- return -1;
- }
- pthread_join(id1,NULL); //等待線程結束
- pthread_join(id2,NULL);
- printf("Running complete!\n");
- return 0;
- }
#include<stdio.h> #include<pthread.h> void *func1(void *arg){ //原函數聲明 int i; for(i=0;i<5;i++){ printf("this is func1! The num is %d\n",*(int*)arg); //將空指針轉換爲int型指針 sleep(1); } } void *func2(int *m){ //自定義類型聲明,也可以定義非指針類型,但是在create時會有警告,因爲非地址並不能改變傳入的值 int i; for(i=0;i<5;i++){ printf("this is func2! The num is %d\n",*m); (*m)++; sleep(1); } } int main(){ pthread_t id1,id2; int num = 5; int *p = # if(pthread_create(&id1,NULL,(void *)func1,(void *)p) != 0){ printf("thread1 create error!\n"); return -1; } if(pthread_create(&id2,NULL,(void *)func2,&num) != 0){ printf("thread2 create error!\n"); return -1; } pthread_join(id1,NULL); //等待線程結束 pthread_join(id2,NULL); printf("Running complete!\n"); return 0; }
運行結果:
- [fsy@localhost process]$ gcc thC.c -o thC -lpthread -g
- [fsy@localhost process]$ ./thC
- this is func2! The num is 5
- this is func1! The num is 6
- this is func2! The num is 6
- this is func1! The num is 7
- this is func2! The num is 7
- this is func1! The num is 8
- this is func2! The num is 8
- this is func1! The num is 9
- this is func2! The num is 9
- this is func1! The num is 10
- Running complete!
- [fsy@localhost process]$
[fsy@localhost process]$ gcc thC.c -o thC -lpthread -g [fsy@localhost process]$ ./thC this is func2! The num is 5 this is func1! The num is 6 this is func2! The num is 6 this is func1! The num is 7 this is func2! The num is 7 this is func1! The num is 8 this is func2! The num is 8 this is func1! The num is 9 this is func2! The num is 9 this is func1! The num is 10 Running complete! [fsy@localhost process]$
2、等待線程[注]當調用pthread_create函數時,線程並沒有開始執行,主進程應有等待,比如用sleep,或者用更專業的函數:pthread_join
int pthread_join(pthread_t tid, void **rval_ptr)
調用函數可以阻塞調用線程,直到指定的線程終止。
tid爲等待退出線程的id,rval_ptr爲函數的返回值。是指向指針的指針,可以置空。
例:
- #include<stdio.h>
- #include<pthread.h>
- #include<stdlib.h>
- void *func(int *p){
- int *num=(int *)malloc(sizeof(int)); //必須動態創建,原因可以參考我動態分配內存的博客
- printf("Please input the number: ");
- scanf("%d",num);
- return (void *)num; //類型是pthread_create的參數規定的
- }
- int main(){
- pthread_t pth;
- void *a;
- if(pthread_create(&pth,NULL,(void *)func,NULL) != 0){
- printf("create thread error!\n");
- return 1;
- }
- pthread_join(pth,&a); //指向空指針的指針
- printf("get the num from the thread, it's %d\n",*(int *)a);
- return 0;
- }
#include<stdio.h> #include<pthread.h> #include<stdlib.h> void *func(int *p){ int *num=(int *)malloc(sizeof(int)); //必須動態創建,原因可以參考我動態分配內存的博客 printf("Please input the number: "); scanf("%d",num); return (void *)num; //類型是pthread_create的參數規定的 } int main(){ pthread_t pth; void *a; if(pthread_create(&pth,NULL,(void *)func,NULL) != 0){ printf("create thread error!\n"); return 1; } pthread_join(pth,&a); //指向空指針的指針 printf("get the num from the thread, it's %d\n",*(int *)a); return 0; }
3、終止線程
線程終止有以下三種方式:
1、線程從函數中返回
2、線程可以別其他函數終止
3、線程自己調用pthread_exit函數
void pthread_exit(void *rval_ptr)
rval_ptr爲線程退出返回值的指針,即函數返回值。
4、退出清除
void pthread_cleanup_push(void (*rtn)(void*), void *arg)
rtn爲清除函數,arg是清除函數的參數
void pthread_cleanup_pop(int execute)
當execute 非0時執行清除函數。爲0時不執行。
從pthread_cleanup_push的調用點到pthread_cleanup_pop之間的程序段中,如果有終止進程的動作,如調用pthread_exit或異常終止(不包括return),就會執行pthread_cleanup_push()所指定的清理函數。多個嵌套匹配時,就近匹配。
例:
- #include<stdio.h>
- #include<pthread.h>
- void *clean(char *argv){
- printf("clean is called by %s\n",argv);
- return NULL;
- }
- void *func1(void *argv){
- printf("welcome enter the func1!\n");
- pthread_cleanup_push((void*)clean,"the first time call!");
- pthread_cleanup_push((void*)clean,"the second time call!");
- if(argv){
- return (void *)1; //第二次運行將此句注掉
- }
- pthread_cleanup_pop(0);
- pthread_cleanup_pop(1);
- return (void *)0;
- }
- void *func2(void *argv){
- sleep(1); //兩個線程運行先後不確定
- printf("welcome enter the func2!\n");
- pthread_cleanup_push((void*)clean,"the first time call!");
- pthread_cleanup_push((void*)clean,"the second time call!");
- if(argv){
- pthread_exit(NULL);
- }
- pthread_cleanup_pop(0);
- pthread_cleanup_pop(0);
- return (void *)0;
- }
- int main(){
- pthread_t tid1,tid2;
- if(pthread_create(&tid1,NULL,(void *)func1,(void *)1) != 0){
- printf("thread1 create error!\n");
- return 1;
- }
- if(pthread_create(&tid2,NULL,(void *)func2,(void *)1) != 0){
- printf("thread2 create error!\n");
- return 1;
- }
- pthread_join(tid1,NULL);
- pthread_join(tid2,NULL);
- return 0;
- }
#include<stdio.h> #include<pthread.h> void *clean(char *argv){ printf("clean is called by %s\n",argv); return NULL; } void *func1(void *argv){ printf("welcome enter the func1!\n"); pthread_cleanup_push((void*)clean,"the first time call!"); pthread_cleanup_push((void*)clean,"the second time call!"); if(argv){ return (void *)1; //第二次運行將此句注掉 } pthread_cleanup_pop(0); pthread_cleanup_pop(1); return (void *)0; } void *func2(void *argv){ sleep(1); //兩個線程運行先後不確定 printf("welcome enter the func2!\n"); pthread_cleanup_push((void*)clean,"the first time call!"); pthread_cleanup_push((void*)clean,"the second time call!"); if(argv){ pthread_exit(NULL); } pthread_cleanup_pop(0); pthread_cleanup_pop(0); return (void *)0; } int main(){ pthread_t tid1,tid2; if(pthread_create(&tid1,NULL,(void *)func1,(void *)1) != 0){ printf("thread1 create error!\n"); return 1; } if(pthread_create(&tid2,NULL,(void *)func2,(void *)1) != 0){ printf("thread2 create error!\n"); return 1; } pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
運行結果:
- [fsy@localhost process]$ gcc thClean.c -o thclean -lpthread
- [fsy@localhost process]$ ./thclean
- welcome enter the func1!
- welcome enter the func2!
- clean is called by the second time call! //此處先2後1
- clean is called by the first time call!
- [fsy@localhost process]$ vim thClean.c
- [fsy@localhost process]$ gcc thClean.c -o thclean -lpthread
- [fsy@localhost process]$ ./thclean
- welcome enter the func1!
- clean is called by the first time call! //second已經被pop
- welcome enter the func2!
- clean is called by the second time call!
- clean is called by the first time call!
- [fsy@localhost process]$