linux 線程學習1

原文:http://www.cnblogs.com/forstudy/archive/2012/04/05/2433853.html

進程
  系統中程序執行和資源分配的基本單位
  每個進程有自己的數據段、代碼段和堆棧段
  在進行切換時需要有比較複雜的上下文切換
 
線程
  減少處理機的空轉時間,支持多處理器以及減少上下文切換開銷, 比創建進程小很多
  進程內獨立的一條運行路線
  處理器調度的最小單元,也稱爲輕量級進程

可以對進程的內存空間和資源進行訪問,並與同一進程中的其他線程共享

 

線程
  線程相關的執行狀態和存儲變量放在線程控制表
  一個進程可以有多個線程,有多個線程控制表及堆棧寄存器,共享一個用戶地址空間
 
多線程同步問題
  線程共享進程的資源和地址空間
  任何線程對系統資源的操作都會給其他線程帶來影響
 

 
線程技術發展
  Linux 2.2內核
    •不存在真正意義上的線程
  Linux 2 .4內核
    •消除線程個數的限制,允許動態地調整進程數上限
  在Linux 內核2.6之前,進程是最主要的處理調度單元,並沒支持內核線程機制
  Linux 2.6內核
    •實現共享地址空間的進程機制, 在1996年第一次獲得線程的支持
 
線程技術發展
  爲了改善LinuxThread問題,根據新內核機制重新編寫線程庫, 改善Linux對線程的支持
    •由IBM主導的新一代POSIX線程庫(Next Generation POSIX Threads,簡稱爲NGPT)
      –NGPT項目在2002年啓動
      –爲了避免出現有多個Linux線程標準,在2003年停止該項目
    •由Red Hat主導的本地化POSIX線程庫 (Native POSIX Thread Library,簡稱爲NTPL)
      –最早在Red Hat Linux9中被支持
      –現在已經成爲GNU C函數庫的一部分,同時也成爲Linux線程的標準
 
 
 
線程標識
  線程ID
    •進程ID在整個系統中是唯一的
    •線程ID只在它所屬的進程環境中有效
函數: pthread_self()

線程標識
  pthread_t類型通常用結構來表示
  •不能把它作爲整數處理
    –Linux使用無符號長整數表示
  •爲了移植,使用函數來比較線程ID
函數: pthread_equal()

複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 int main(){
 6     pthread_t thread_id;
 7 
 8     thread_id=pthread_self(); // 返回調用線程的線程ID
 9     printf("Thread ID: %lu.\n",thread_id);
10 
11     if (pthread_equal(thread_id,pthread_self())) {
12 //    if (thread_id==0) {
13         printf("Equal!\n");
14     } else {
15         printf("Not equal!\n");
16     }
17     return 0;
18 }
複製代碼

 

 

線程編程
  操作用戶空間中的線程
 
創建線程
  •調用該線程函數的入口點
  •使用函數pthread_create(),線程創建後,就開始運行相關的線程函數
複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 void *thrd_func(void *arg);
 6 pthread_t tid;
 7 
 8 int main(){
 9     // 創建線程tid,且線程函數由thrd_func指向,是thrd_func的入口點,即馬上執行此線程函數
10     if (pthread_create(&tid,NULL,thrd_func,NULL)!=0) {
11         printf("Create thread error!\n");
12         exit(1);
13     }
14 
15     printf("TID in pthread_create function: %u.\n",tid);
16     printf("Main process: PID: %d,TID: %u.\n",getpid(),pthread_self()); 
17     
18     sleep(1); //race
19 
20     return 0;
21 }
22 
23 void *thrd_func(void *arg){
24 //    printf("I am new thread!\n");
25     printf("New process:  PID: %d,TID: %u.\n",getpid(),pthread_self()); //why pthread_self
26     printf("New process:  PID: %d,TID: %u.\n",getpid(),tid); //why pthread_self
27 
28     pthread_exit(NULL); //退出線程
29 //    return ((void *)0);
30 }
複製代碼

 
退出線程
  •在線程函數運行完後,該線程也就退出了
  •或使用函數pthread_exit(),這是線程的主動行爲
  •不能使用exit()

使調用進程終止,所有線程都終止了

等待線程

  •由於一個進程中的多個線程是共享數據段的,通常在線程退出之後,退出線程所佔用的資源並不會隨着線程的終止而得到釋放

  •pthread_join()函數

    類似進程的wait()/waitpid()函數,用於將當前線程掛起來等待線程的結束
    是一個線程阻塞的函數,調用它的線程一直等待到被等待的線程結束爲止
    函數返回時,被等待線程的資源就被收回

複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 void *thrd_func1(void *arg);
 6 void *thrd_func2(void *arg);
 7 
 8 int main(){
 9     pthread_t tid1,tid2;
10     void *tret;
11     // 創建線程tid1,線程函數thrd_func1
12     if (pthread_create(&tid1,NULL,thrd_func1,NULL)!=0) {
13         printf("Create thread 1 error!\n");
14         exit(1);
15     }
16     // 創建線程tid2,線程函數thrd_func2
17     if (pthread_create(&tid2,NULL,thrd_func2,NULL)!=0) {
18         printf("Create thread 2 error!\n");
19         exit(1);
20     }
21     // 等待線程tid1結束,線程函數返回值放在tret中
22     if (pthread_join(tid1,&tret)!=0){
23         printf("Join thread 1 error!\n");
24         exit(1);
25     }
26 
27     printf("Thread 1 exit code: %d.\n",(int *)tret);
28     // 等待tid2結束,線程函數返回值放在tret中
29     if (pthread_join(tid2,&tret)!=0){
30         printf("Join thread 2 error!\n");
31         exit(1);
32     }
33 
34     printf("Thread 2 exit code: %d.\n",(int *)tret);
35 
36     return 0;
37 }
38 
39 void *thrd_func1(void *arg){
40     printf("Thread 1 returning!\n");
41 //    sleep(3);
42     return ((void *)1); // 自動退出線程
43 }
44 
45 void *thrd_func2(void *arg){
46     printf("Thread 2 exiting!\n");
47     pthread_exit((void *)2);  // 線程主動退出,返回(void *)2
48 }
複製代碼



取消線程

  •在別的線程中要終止另一個線程
  •pthread_cancel()函數
  •被取消的線程可以設置自己的取消狀態
    –被取消的線程接收到另一個線程的取消請求之後,是接受還是忽略這個請求
    –如果接受,是立刻進行終止操作還是等待某個函數的調用等

複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 void *thrd_func1(void *arg);
 6 void *thrd_func2(void *arg);
 7 
 8 pthread_t tid1,tid2;
 9 
10 int main(){
11     // 創建線程tid1,線程函數thrd_func1
12     if (pthread_create(&tid1,NULL,thrd_func1,NULL)!=0) {
13         printf("Create thread 1 error!\n");
14         exit(1);
15     }
16     // 創建線程tid2,線程函數thrd_func2
17     if (pthread_create(&tid2,NULL,thrd_func2,NULL)!=0) {
18         printf("Create thread 2 error!\n");
19         exit(1);
20     }
21     // 等待線程tid1退出
22     if (pthread_join(tid1,NULL)!=0){
23         printf("Join thread 1 error!\n");
24         exit(1);
25     }else
26         printf("Thread 1 Joined!\n");
27     // 等待線程tid2退出
28     if (pthread_join(tid2,NULL)!=0){
29         printf("Join thread 2 error!\n");
30         exit(1);
31     }else
32         printf("Thread 2 Joined!\n");
33 
34     return 0;
35 }
36 
37 void *thrd_func1(void *arg){
38 //    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
39     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 設置其他線程可以cancel掉此線程
40 
41     while(1) {
42         printf("Thread 1 is running!\n");
43         sleep(1);
44     }
45     pthread_exit((void *)0);
46 }
47 
48 void *thrd_func2(void *arg){
49     printf("Thread 2 is running!\n");
50     sleep(5);
51     if (pthread_cancel(tid1)==0)  // 線程tid2向線程tid1發送cancel
52         printf("Send Cancel cmd to Thread 1.\n");
53         
54     pthread_exit((void *)0);
55 }
複製代碼

 

複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 
 5 #define THREAD_NUM 3
 6 #define REPEAT_TIMES 5
 7 #define DELAY 4
 8 
 9 void *thrd_func(void *arg);
10 
11 int main(){
12     pthread_t thread[THREAD_NUM];
13     int no;
14     void *tret;
15     
16     srand((int)time(0)); // 初始化隨機函數發生器 
17 
18     for(no=0;no<THREAD_NUM;no++){
19         if (pthread_create(&thread[no],NULL,thrd_func,(void*)no)!=0) { // 創建THREAD_NUM個線程,傳入(void*)no作爲thrd_func的參數
20             printf("Create thread %d error!\n",no);
21             exit(1);
22         } else
23             printf("Create thread %d success!\n",no);
24     }
25 
26     for(no=0;no<THREAD_NUM;no++){
27         if (pthread_join(thread[no],&tret)!=0){ // 等待thread[no]線程結束,線程函數返回值放在tret中
28             printf("Join thread %d error!\n",no);
29             exit(1);
30         }else
31             printf("Join thread %d success!\n",no);
32     }
33         
34     return 0;
35 }
36 
37 void *thrd_func(void *arg){
38     int thrd_num=(void*)arg;
39     int delay_time=0;
40     int count=0;
41 
42     printf("Thread %d is starting.\n",thrd_num);
43     for(count=0;count<REPEAT_TIMES;count++) {
44         delay_time=(int)(DELAY*(rand()/(double)RAND_MAX))+1;
45         sleep(delay_time);
46         printf("\tThread %d:job %d delay =%d.\n",thrd_num,count,delay_time);
47     }
48 
49     printf("Thread %d is exiting.\n",thrd_num);
50     pthread_exit(NULL);
51 }
複製代碼



 

線程同步與互斥
  線程共享進程的資源和地址空間,對這些資源進行操作時,必須考慮線程間同步與互斥問題
  三種線程同步機制
    •互斥鎖
    •信號量
    •條件變量
  互斥鎖更適合同時可用的資源是惟一的情況
    信號量更適合同時可用的資源爲多個的情況 

 

 

互斥鎖
  用簡單的加鎖方法控制對共享資源的原子操作
  只有兩種狀態: 上鎖、解鎖
可把互斥鎖看作某種意義上的全局變量
  在同一時刻只能有一個線程掌握某個互斥鎖,擁有上鎖狀態的線程能夠對共享資源進行操作
  若其他線程希望上鎖一個已經被上鎖的互斥鎖,則該線程就會掛起,直到上鎖的線程釋放掉互斥鎖爲止
互斥鎖保證讓每個線程對共享資源按順序進行原子操作

 

 

互斥鎖分類
  區別在於其他未佔有互斥鎖的線程在希望得到互斥鎖時是否需要阻塞等待
  快速互斥鎖
    •調用線程會阻塞直至擁有互斥鎖的線程解鎖爲止
    •默認爲快速互斥鎖
  檢錯互斥鎖
    •爲快速互斥鎖的非阻塞版本,它會立即返回並返回一個錯誤信息
 
互斥鎖主要包括下面的基本函數:
  互斥鎖初始化:pthread_mutex_init()
  互斥鎖上鎖:pthread_mutex_lock()
  互斥鎖判斷上鎖:pthread_mutex_trylock()
  互斥鎖解鎖:pthread_mutex_unlock()
  消除互斥鎖:pthread_mutex_destroy()
 

 

View Code

和上一版本的程序差異在於有沒有鎖,有鎖的情況下,必須等"thread x is exiting."之後其他線程才能繼續。

 

 

信號量
  操作系統中所用到的PV原子操作,廣泛用於進程或線程間的同步與互斥
    •本質上是一個非負的整數計數器,被用來控制對公共資源的訪問
  PV原子操作:對整數計數器信號量sem的操作
    •一次P操作使sem減一,而一次V操作使sem加一
    •進程(或線程)根據信號量的值來判斷是否對公共資源具有訪問權限
  –當信號量sem的值大於等於零時,該進程(或線程)具有公共資源的訪問權限
  –當信號量sem的值小於零時,該進程(或線程)就將阻塞直到信號量sem的值大於等於0爲止
 
PV操作主要用於線程間的同步和互斥
  互斥,幾個線程只設置一個信號量sem
  同步,會設置多個信號量,安排不同初值來實現它們之間的順序執行

 

信號量函數
  sem_init() 創建一個信號量,並初始化它
  sem_wait()和sem_trywait(): P操作,在信號量大於零時將信號量的值減一
    •區別: 若信號量小於零時,sem_wait()將會阻塞線程,sem_trywait()則會立即返回
  sem_post(): V操作,將信號量的值加一同時發出信號來喚醒等待的線程
  sem_getvalue(): 得到信號量的值
  sem_destroy(): 刪除信號量

 

eg. 同步各線程,執行順序爲逆序。

View Code

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章