1,概念: 進程:一個正在執行的程式,他是資源分配的最小單位。進程中的事情語言按照一定順序逐一進行 線程:又稱輕量級線程,程式執行的最小單位,系統獨立調度和分派CPU的基本單位,他是進程中一個實體,一個進程中可以有多個線程,這些線程共用進程的所有資 源,線程本身只包含一點必不可少的資源。 併發:指在 ...
1,概念:
進程:一個正在執行的程式,他是資源分配的最小單位。進程中的事情語言按照一定順序逐一進行
線程:又稱輕量級線程,程式執行的最小單位,系統獨立調度和分派CPU的基本單位,他是進程中一個實體,一個進程中可以有多個線程,這些線程共用進程的所有資 源,線程本身只包含一點必不可少的資源。
併發:指在同一時刻,只能有執行一條指令,但多個線程指令被快速輪換執行,使得巨集觀上具有多個進程同時執行的效果 。 (看起來同時發生,針對單核)
並行:指在同一時刻,有多條指令在多個處理器上同時執行。 (真正的同時發生)
同步:彼此有依賴關係的調用不應該“同時發生”,而同步就是阻止那些“同時發生”的事情
非同步:與同步相對,任何兩個彼此獨立的操作時非同步的,它表明事情獨立發生
多線程的優勢:
1)在多處理器中開發程式的並行性
2)在等待慢速IO操作時,程式可以執行其他操作,提高併發性
3)模塊化的編程,能更清晰的表達程式中獨立事件的關係,結構清晰
4)占用較少的系統資源
多線程不一定要多處理器
線程創造 | 獲取ID,生命周期 |
線程式控制制 | 終止、連接、取消、發送信號、清除操作 |
線程同步 | 互斥量、讀寫鎖、條件變數 |
線程高級控制 | 一次性初始化、線程屬性、同步屬性、私有數據、安全的fork() |
2,線程的基本控制
創建新線程
線程ID
線程 | 進程 | |
標識符類型 | pthread_t | pid_t |
獲取ID | pthread_seif() | getpid() |
創建 | pthread_create() | fork() |
pthread_t : 結構體 unsigned long int (linux 中提高移植性) /usr/include/bits/pthreadtypes.h
獲取線程ID : pthread_self()
頭文件: #include <pthread.h>
函數: pthread_t pthread_seif();
返回值 : 線程ID
編譯鏈接時需要用到線程庫 -pthread ::gcc -pthread (用到線程都要用)
創建線程:ptnread_create()
函數: int ptnread_create ( ptnread_t *restrict tidp, const ptnread_attr_t *restrict attr , void *(*start_routine)(void *) , void *restrict arg)
參數: 第一個參數: 新線程的ID,如果成功則新線程的ID回填充到tipe 指向的記憶體
第二個參數 : 線程屬性 (調整策略,繼承性,分離性...)
第三個參數 : 回調函數 (新線程要執行的函數)
第四個參數 : 回調函數的參數
返回值 : 成功 0 ; 失敗則返回錯誤碼
編譯時需要連接庫 -pthread 主線程結束後必須有延時讓其創建新線程,不然主進程直接被返回。方法用延時或者 下述2
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <string.h> 7 void print_id(char *s) 8 { 9 pid_t pid; 10 pthread_t tid; 11 12 pid = getpid(); 13 tid = pthread_self(); 14 15 printf("%s pid is %u,the tid is %lu\n",s,pid,tid); 16 17 } 18 19 void *thread_fun (void *arg) 20 { 21 print_id(arg); 22 return (void *)0; 23 } 24 25 int main() 26 { 27 pthread_t ntid; 28 int err; 29 30 err = pthread_create (&ntid, NULL , thread_fun ,"new thread"); 31 32 if(err!=0) 33 { 34 printf("create new thread failure\n"); 35 return 0; 36 } 37 // printf("create new thread sucess\n"); 38 print_id("main thread:"); 39 sleep(2); 40 return 0; 41 } 42 ~ 43 44 結果: 45 main thread: pid is 2833,the tid is 3076491008 46 new thread pid is 2833,the tid is 3076488000進程創建
線程的生命周期
1,當C程式運行時,首先運行main()函數;線上程代碼中,這個特殊的執行流被稱為初始線程或者主線程。可以在初始線程中做任何普通線程可以做的事情
2,主線程的特殊性在於,它在main() 函數返回的時候,會導致進程的結束,進程內所有的線程也將會結束。為了避免這種情況,需要在主線程中調用 pthread_exit() 函數,這樣進程就會等所有線程結束時才終止。
3,主線程接收參數的方式是通過argc 和argv[] ;而普通線程只有一個參數 void*
4,絕大多數情況下,主線程在預設堆棧上運行,這個堆棧可以 延長,而普通線程的堆棧會收到限制,一旦溢出就會出錯
5,主線程是隨著進程的創建而創建,其他線程可以通過調用函數來創建 ,主要為 pthread_create();
6,新線程可能在當前線程從函數 pthread_cteate () 返回之前就已經開始運行,甚至可能在返回之前就已經運行完畢
1 #include "stdio.h" 2 #include "pthread.h" 3 #include "stdlib.h" 4 #include "unistd.h" 5 #include "sys/types.h" 6 #include "string.h" 7 8 struct student 9 { 10 int age; 11 char name[20]; 12 char id[5]; 13 }; 14 15 void *thread(void *s) 16 { 17 18 printf("student age is %d,name is %s,id is %s\n",((struct student *)s)->age,((struct student *)s)->name,((struct student *)s)->id); 19 return (void *)0; 20 21 } 22 23 24 int main(int argc,char *argv[]) 25 { 26 27 pthread_t tid; 28 int err; 29 30 31 struct student stu; 32 stu.age =20; 33 memcpy(stu.name , "zhangsan" , 20); 34 memcpy(stu.id , "0001" , 5); 35 err = pthread_create(&tid , NULL , thread , (void *)(&stu)); 36 if(err != 0) 37 { 38 39 printf("creat new thread failure\n"); 40 return 0; 41 42 } 43 int i; 44 for(i=0;i<argc;i++) 45 { 46 printf("main thread args is %s\n",argv[i]); 47 48 } 49 sleep(1); 50 return 0; 51 52 } 53 ~ 54 結果: 55 root@ubuntu:/home/xiaozhao# gcc -pthread b.c -o b 56 root@ubuntu:/home/xiaozhao# ./b 21 32 42 57 main thread args is ./b 58 main thread args is 21 59 main thread args is 32 60 main thread args is 42 61 student age is 20,name is zhangsan,id is 0001驗證主進程的特殊
1 #include "stdio.h" 2 #include "pthread.h" 3 #include "stdlib.h" 4 #include "unistd.h" 5 #include "sys/types.h" 6 #include "string.h" 7 8 void *thread(void *s) 9 { 10 int i=0; 11 while(1) 12 { 13 if(i%2 ==1) 14 15 printf("new thread is %d\n",i); 16 i++; 17 sleep(1); 18 } 19 return (void *)0; 20 21 } 22 23 24 int main(int argc,char *argv[]) 25 { 26 27 pthread_t tid; 28 int err; 29 30 err = pthread_create(&tid , NULL , thread , "new thread"); 31 if(err != 0) 32 { 33 34 printf("creat new thread failure\n"); 35 return 0; 36 } 37 int i=0; 38 while(i<10) 39 { 40 if(i%2 == 0) 41 printf( "main thread is %d\n",i); 42 i++; 43 sleep(1); 44 } 45 return 0; 46 47 } 48 49 50 結果: 51 root@ubuntu:/home/xiaozhao# ./c 52 main thread is 0 53 new thread is 1 54 main thread is 2 55 new thread is 3 56 main thread is 4 57 new thread is 5 58 main thread is 6 59 new thread is 7 60 main thread is 8 61 new thread is 9 62 63 64 65 66 註意: 67 主線程迴圈結束,新線程也被結束;而新線程迴圈結束,不會影響主線程 68主線程和新線程交替列印奇偶
線程的四個狀態
就緒: 當線程剛被創建處於就緒狀態,或者當線程解除阻塞以後也會處於就緒狀態。就緒的線程在等待一個可用的處理器,當一個運行的線程被搶占時,它立即又回到就緒狀態
運行 :線程正在運行,在多核系統中,可能同時又多個線程在運行
阻塞: 線程在等待處理器意外的其他條件(試圖加鎖一個已經被鎖住的互斥量,等待某個條件變數,調用singwait 等待尚未發生的信號,執行無法完成的I\O信號,由於記憶體錯誤)
終止: 線程從啟動函數中返回,或者調用 pthread_exit() 函數,或者被取消
回收
線程的分離屬性:
分離一個正在運行的線程並不影響它,僅僅是通知當前系統該線程結束的時,其所屬的資源可以回收,一個沒有被分離的線程在終止時會保留其虛擬記憶體,包括他們的堆棧和其他系統資源,有時這種線程被稱為“僵屍進程”。創建線程時預設時非分離的。
如果線程具有分離屬性,線程終止時會被立刻回收,回收將釋放掉所有線上程終止時未被釋放的系統資源和進程資源,包括保存線程返回值的記憶體空間、堆棧、保存寄存器的記憶體空間等
終止被分離的線程會釋放所有的系統資源,但是必須釋放有該線程占有的程式資源。由malloc() 或 mmap() 分配的記憶體可以在任何時候由任何線程釋放,條件變數、互斥量、信號燈可以由任何線程銷毀,只要他們被解鎖了或者沒有線程等待,但是只有互斥量的主人才能解鎖它,所以線上程終止前,需要解鎖互斥量
*********************************************************************************************************************************
線程終止
1, 如果進程中的任意一個線程調用了exit() , _Exit ,_exit , 那麼整個進程就會終止
2,不終止進程的退出方式:
普通的單個線程有以下三種方式退出,不會終止進程:
- 從啟動歷程中返回,返回值是線程的退出碼 return
- 線程可以被同一進程中的其他線程取消
- 線程調用pthread_exit(void *rval) 函數,rval 是退出碼
1 #include "stdio.h" 2 #include "pthread.h" 3 #include "stdlib.h" 4 #include "unistd.h" 5 #include "sys/types.h" 6 #include "string.h" 7 8 void *thread_fun (void *s) 9 { 10 if(strcmp("1",(char *)s)==0) 11 return (void *)1; 12 13 if(strcmp("2",(char *)s)==0) 14 pthread_exit((void *)2); 15 16 if(strcmp("3",(char *)s)==0) 17 exit(3); 18 } 19 20 21 int main(int argc, char *argv[]) 22 { 23 pthread_t tid; 24 int err; 25 26 err = pthread_create (&tid , NULL , thread_fun , (void *)argv[1]); 27 if (err !=0) 28 { 29 printf("create new thread failure\n"); 30 return 0; 31 } 32 sleep(1); 33 printf("my is main thread\n"); 34 return 0; 35 36 } 37 結果: 38 root@ubuntu:/home/xiaozhao# ./d 1 39 my is main thread 40 root@ubuntu:/home/xiaozhao# ./d 2 41 my is main thread 42 root@ubuntu:/home/xiaozhao# ./d 3 43 root@ubuntu:/home/xiaozhao# 44 45 46 說明:exit()會直接導致進程退出,而前兩種不會三種退出方式
線程連接(一個進程等待另一個進程完成在結束)
int pthread_join (pthread_t tid , void **rval)
- 調用該函數的線程會一直阻塞,直到指定的線程tid 調用 pthread_exit () ; 從啟動歷程返回或者被取消
- 參數tid 就是指定線程的ID
- 參數ravl 是指定線程的返回碼,如果線程被取消,那麼ravl 被置為 PTHREAD_CANCELED
- 該函數調用成功會返回0,失敗返回錯誤碼
調用pthread_join 會使指定的線程處於分離狀態,如果指定線程已經處於分離狀態,那麼調用就會失敗
pthread_detach () 可以分離一個線程,線程可以自己分離自己
int pthread_datach (pthread_t thread);
成功返回0 ; 失敗返回錯誤碼
1 #include "apb.h" 2 3 void *thread_fun1 (void *s) 4 { 5 printf("my is new thread1\n"); 6 return (void *)1; 7 8 } 9 10 void *thread_fun2 (void *s) 11 { 12 13 printf("my is new thread2\n"); 14 pthread_detach(pthread_self()); 15 pthread_exit((void *)2); 16 } 17 18 int main() 19 { 20 int err1,err2; 21 pthread_t tid1,tid2; 22 void *rval1,*rval2; 23 24 25 err1 = pthread_create (&tid1 , NULL , thread_fun1 , NULL ); 26 sleep(1); 27 err2 = pthread_create (&tid2 , NULL , thread_fun2 , NULL ); 28 sleep(1); 29 if( err1 || err2 ) 30 { 31 printf("create new thread failure\n"); 32 return 0; 33 } 34 35 printf("my is main thread\n"); 36 printf("join rval1 is %d\n",pthread_join(tid1,&rval1)); 37 printf("join rval2 is %d\n",pthread_join(tid2,&rval2)); 38 39 printf("thread1 exit is %d\n",(int )rval1); 40 printf("thread2 exit is %d\n",(int )rval2); 41 42 43 return 0; 44 } 45 ~ 46 47 結果: 48 my is new thread1 49 my is new thread2 50 my is main thread 51 join rval1 is 0 52 join rval2 is 22 53 thread1 exit is 1 54 thread2 exit is -1216983040 55 56 問題: 57 如果創建之後不加延時,線程二會比線程一早完成,輸出早。。目前不知原因及解決辦法 58 my is main thread 59 my is new thread2 60 my is new thread1 61 join rval1 is 0 62 join rval2 is 22 63 thread1 exit is 1 64 thread2 exit is -1216983040連接,分離
線程取消
取消函數
int pthread_cancel (pthread_t tid);
取消tid指定的線程,成功返回0.但是取消只是發送一個請求,並不意味著等待線程終止,而且發送成功也不意味著tid一定會終止。
取消狀態
取消狀態就是線程對取消信號的處理方式,忽略或者響應。線程創建時預設響應取消信號
int pthread_setcancelstate (int state , int *oldstate);
設置本線程對cancel 信號的反應,state有兩種值 : PTHREAD_CANCEL_ENABLE(預設)和PTHREAD_CANCEL_DISABLE。。分別表示收到信號後設為CANCLED狀態和忽略CANCEL信號 繼續運行;old_state 如果不為NULL ,則存入原來的CANCEL 狀態以便恢復
取消類型
取消類型時線程對取消信號的響應方式,立即取消或者延時取消。線程創建時預設為延時取消
int pthread_setcanceltype (int type , int *oldtype);
設置本線程取消動作的執行時機,type由兩種取值: PTHREAD_CANCEL_DEFFERED 和 PTHREAD_CANCEL_ASYCHRONOUS , 僅當cancel 狀態 為ENABLE時有效,分別表示收到信號後繼續運行至下一個取消點再退出,和 立即執行取消動作 (退出); oldtype 如果不為NULL ,則存入運來的取消動作類型值。
取消點
取消一個線程,他通常需要被取消線程的配合。線程在很多時候會查看自己是否由取消請求。如果有,就主動退出,這些查看是否有取消的地方成為取消點
很多地方都是包含取消點,包括 pthread_join() , pthread_teacancel() , 等等大多數會阻塞的系統調用
1 線程被取消 2 #include "apb.h" 3 4 void *thread_fun (void *s) 5 { 6 int stateval; 7 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL); 8 if (stateval != 0) 9 { 10 printf("set cancel state failure\n"); 11 } 12 printf("my is new thread\n"); 13 sleep(4); 14 15 printf("about to cancel\n"); 16 stateval = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE , NULL); 17 printf("first cancel point\n"); 18 printf("secong cancel point\n"); 19 return (void *)20; 20 21 22 } 23 24 25 26 int main() 27 { 28 pthread_t tid; 29 int err,cval; 30 int jval; 31 void *rval; 32 33 err = pthread_create (&tid , NULL , thread_fun , NULL); 34 if (err != 0) 35 { 36 printf("create new thread failure\n"); 37 return 0; 38 } 39 sleep(2); 40 cval = pthread_cancel(tid); 41 if(cval !=0) 42 { 43 printf("cancel thread failure\n"); 44 } 45 jval = pthread_join(tid, &rval); 46 47 printf("cancel rval is %d\n",(int )rval); 48 return 0; 49 50 51 } 52 53 結果: 54 my is new thread 55 about to cancel 56 first cancel point 57 cancel rval is -1 58 59 60 線程忽略取消信號 61 62 #include "apb.h" 63 64 void *thread_fun (void *s) 65 { 66 int stateval; 67 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL); 68 if (stateval != 0) 69 { 70 printf("set cancel state failure\n"); 71 } 72 printf("my is new thread\n"); 73 sleep(4); 74 75 printf("about to cancel\n"); 76 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL); 77 printf("first cancel point\n"); 78 printf("secong cancel point\n"); 79 return (void *)20; 80 81 82 } 83 84 85 86 int main() 87 { 88 pthread_t tid; 89 int err,cval; 90 int jval; 91 void *rval; 92 93 err = pthread_create (&tid , NULL , thread_fun , NULL); 94 if (err != 0) 95 { 96 printf("create new thread failure\n"); 97 return 0; 98 } 99 sleep(2); 100 cval = pthread_cancel(tid); 101 if(cval !=0) 102 { 103 printf("cancel thread failure\n"); 104 } 105 jval = pthread_join(tid, &rval); 106 107 printf("cancel rval is %d\n",(int )rval); 108 return 0; 109 110 111 } 112 結果: 113 114 my is new thread 115 about to cancel 116