C/C++ Pthread線程


線程按照其調度者可以分為用戶級線程和核心級線程兩種 
用戶級線程主要解決的是上下文切換的問題,它的調度算法和調度過程全部由用戶自行選擇決定,在運行時不需要特定的內核支持; 
我們常用基本就是用戶級線程,所以就只總結一下POSIX提供的用戶級線程接口; 
基本線程操作相關的函數: 
1線程的建立結束 
2線程的互斥和同步 
3使用信號量控制線程 
4線程的基本屬性配置 

基本線程操作:

函數 說明
pthread_create() 創建線程開始運行相關線程函數,運行結束則線程退出
pthread_eixt() 因為exit()是用來結束進程的,所以則需要使用特定結束線程的函數
pthread_join() 掛起當前線程,用於阻塞式地等待線程結束,如果線程已結束則立即返回,0=成功
pthread_cancel() 發送終止信號給thread線程,成功返回0,但是成功並不意味着thread會終止
pthread_testcancel() 在不包含取消點,但是又需要取消點的地方創建一個取消點,以便在一個沒有包含取消點的執行代碼線程中響應取消請求.
pthread_setcancelstate() 設置本線程對Cancel信號的反應
pthread_setcanceltype() 設置取消狀態 繼續運行至下一個取消點再退出或者是立即執行取消動作
pthread_setcancel() 設置取消狀態


互斥與同步機制基本函數

函數 說明
pthread_mutex_init() 互斥鎖的初始化
pthread_mutex_lock() 鎖定互斥鎖,如果嘗試鎖定已經被上鎖的互斥鎖則阻塞至可用為止
pthread_mutex_trylock() 非阻塞的鎖定互斥鎖
pthread_mutex_unlock() 釋放互斥鎖
pthread_mutex_destory() 互斥鎖銷毀函數


信號量線程控制(默認無名信號量)

函數 說明
sem_init(sem) 初始化一個定位在sem的匿名信號量
sem_wait() 把信號量減1操作,如果信號量的當前值為0則進入阻塞,為原子操作
sem_trywait() 如果信號量的當前值為0則返回錯誤而不是阻塞調用(errno=EAGAIN),其實是sem_wait()的非阻塞版本
sem_post() 給信號量的值加1,它是一個“原子操作”,即同時對同一個信號量做加1,操作的兩個線程是不會沖突的
sem_getvalue(sval) 把sem指向的信號量當前值放置在sval指向的整數上
sem_destory(sem) 銷毀由sem指向的匿名信號量


線程屬性配置相關函數

函數 說明
pthread_attr_init() 初始化配置一個線程對象的屬性,需要用pthread_attr_destroy函數去除已有屬性
pthread_attr_setscope() 設置線程屬性
pthread_attr_setschedparam() 設置線程優先級
pthread_attr_getschedparam() 獲取線程優先級

 

基本的線程建立運行pthread_create

/* thread.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREAD_NUMBER       3                 /*線程數*/
#define REPEAT_NUMBER       5                 /*每個線程中的小任務數*/
#define DELAY_TIME_LEVELS  10.0             /*小任務之間的最大時間間隔*/
//
void *thrd_func(void *arg) { 
    /* 線程函數例程 */
    int thrd_num = (int)arg;
    int delay_time = 0;
    int count = 0;
    printf("Thread %d is starting\n", thrd_num);
    for (count = 0; count < REPEAT_NUMBER; count++) {
        delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
        sleep(delay_time);
        printf("\tThread %d: job %d delay = %d\n", 
                      thrd_num, count, delay_time);
    }

    printf("Thread %d finished\n", thrd_num);
    pthread_exit(NULL);
}

int main(void) {
     pthread_t thread[THREAD_NUMBER];
     int no = 0, res;
     void * thrd_ret;
     srand(time(NULL));    
     for (no = 0; no < THREAD_NUMBER; no++) {
          /* 創建多線程 */
          res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
          if (res != 0) {
               printf("Create thread %d failed\n", no);
               exit(res);
          }
     }

     printf("Create treads success\n Waiting for threads to finish...\n");
     for (no = 0; no < THREAD_NUMBER; no++) {
          /* 等待線程結束 */
          res = pthread_join(thread[no], &thrd_ret);
          if (!res) {
            printf("Thread %d joined\n", no);
          } else {
            printf("Thread %d join failed\n", no);
          }
     }
     return 0;        
}

例程中循環3次建立3條線程,並且使用pthread_join函數依次等待線程結束; 
線程中使用rand()獲取隨機值隨機休眠5次,隨意會出現后執行的線程先執行完成; 
運行結果:

$ gcc thread.c -lpthread
$ ./a.out 
Create treads success
 Waiting for threads to finish...
Thread 0 is starting
Thread 1 is starting
Thread 2 is starting
Thread 1: job 0 delay = 2
Thread 1: job 1 delay = 2
Thread 0: job 0 delay = 8
Thread 2: job 0 delay = 10
Thread 2: job 1 delay = 3
Thread 1: job 2 delay = 10
Thread 0: job 1 delay = 8
Thread 0: job 2 delay = 3
Thread 0: job 3 delay = 1
Thread 2: job 2 delay = 8
Thread 1: job 3 delay = 8
Thread 1: job 4 delay = 1
Thread 1 finished
        Thread 2: job 3 delay = 6
        Thread 0: job 4 delay = 7
Thread 0 finished
Thread 0 joined
Thread 1 joined
        Thread 2: job 4 delay = 10
Thread 2 finished
Thread 2 joined

可以看到,線程1先於線程0執行,但是pthread_join的調用時間順序,先等待線程0執行; 
由於線程1已經早結束,所以線程0被pthread_join等到的時候,線程1已結束,就在等待到線程1時,直接返回; 

線程執行的互斥和同步pthread_mutex_lock

在上面的程序中增加互斥鎖

/*thread_mutex.c*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREAD_NUMBER        3            /* 線程數 */
#define REPEAT_NUMBER        3            /* 每個線程的小任務數 */
#define DELAY_TIME_LEVELS 10.0         /*小任務之間的最大時間間隔*/
pthread_mutex_t mutex;

void *thrd_func(void *arg) {
     int thrd_num = (int)arg;
     int delay_time = 0, count = 0;
     int res;
     /* 互斥鎖上鎖 */
     res = pthread_mutex_lock(&mutex);
     if (res) {
          printf("Thread %d lock failed\n", thrd_num);
          pthread_exit(NULL);
     }
     printf("Thread %d is starting\n", thrd_num);
     for (count = 0; count < REPEAT_NUMBER; count++) {          
         delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
         sleep(delay_time);
         printf("\tThread %d: job %d delay = %d\n", 
                                      thrd_num, count, delay_time);
     }
     printf("Thread %d finished\n", thrd_num);
     pthread_exit(NULL);
}

int main(void) {
     pthread_t thread[THREAD_NUMBER];
     int no = 0, res;
     void * thrd_ret;

     srand(time(NULL));
     /* 互斥鎖初始化 */
     pthread_mutex_init(&mutex, NULL);
     for (no = 0; no < THREAD_NUMBER; no++) {
          res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
          if (res != 0) {
              printf("Create thread %d failed\n", no);
              exit(res);
          }
     }     
     printf("Create treads success\n Waiting for threads to finish...\n");
     for (no = 0; no < THREAD_NUMBER; no++) {
          res = pthread_join(thread[no], &thrd_ret);
          if (!res) {
                printf("Thread %d joined\n", no);
          } else  {
              printf("Thread %d join failed\n", no);
          }
     }   
     /****互斥鎖解鎖***/
     pthread_mutex_unlock(&mutex);
     pthread_mutex_destroy(&mutex);          
     return 0;        
}

在上面的例程中直接添加同步鎖pthread_mutex_t; 
在線程中加入,於是程序在執行線程程序時; 
調用pthread_mutex_lock上鎖,發現上鎖時候后進入等待,等待鎖再次釋放后重新上鎖; 
所以線程程序加載到隊列中等待,等待成功上鎖后繼續執行程序代碼; 
運行結果

$gcc thread_mutex.c -lpthread
$ ./a.out 
Create treads success
 Waiting for threads to finish...
Thread 0 is starting
        Thread 0: job 0 delay = 9
        Thread 0: job 1 delay = 4
        Thread 0: job 2 delay = 7
Thread 0 finished
Thread 0 joined
Thread 1 is starting
        Thread 1: job 0 delay = 6
        Thread 1: job 1 delay = 4
        Thread 1: job 2 delay = 7
Thread 1 finished
Thread 1 joined
Thread 2 is starting
        Thread 2: job 0 delay = 3
        Thread 2: job 1 delay = 1
        Thread 2: job 2 delay = 6
Thread 2 finished
Thread 2 joined

跟例程1中執行結果不同,線程程序被加載到隊列中而不能馬上執行,需要等到能夠成功上鎖; 
上鎖后,繼續執行線程程序,上鎖執行; 
這樣線程被依次執行的情況在實際使用場景中經常出現; 
使用場景: 
當用戶登錄后獲取秘鑰才能繼續獲取該用戶的基本信息時;需要等待登錄線程結束后才能繼續執行獲取用戶信息的線程時, 
需要調用兩條線程,假如是:threadLogin(),threadGetInfo(); 則可以有2種方法實現: 
1 此時可以使用互斥鎖同時一次性調用完threadLogin()和threadGetInfo(); 
2 當然也可以不使用互斥鎖直接在threadLogin()中登錄驗證成功后調用threadGetInfo(); 
相比之下,方式1更加清晰的顯示邏輯關系,增加代碼可讀性可擴展性。

 

使用信號量控制線程的執行順序sem_post

修改上面例程,上面的是使用pthread_mutex_lock互斥鎖控制線程執行順序, 
使用另外一種線程執行順序的控制;

/* thread_sem.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

#define THREAD_NUMBER       3
#define REPEAT_NUMBER       3
#define DELAY_TIME_LEVELS   10.0

sem_t sem[THREAD_NUMBER];

void * thrd_func(void *arg) {
    int thrd_num = (int)arg;
    int delay_time = 0;
    int count = 0;
    sem_wait(&sem[thrd_num]);
    printf("Thread %d is starting\n", thrd_num);
    for (count = 0; count < REPEAT_NUMBER; count++) {
        delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
        sleep(delay_time);
        printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);
    }
    printf("Thread %d finished\n", thrd_num);
    pthread_exit(NULL);
}

int main(void) {
    pthread_t thread[THREAD_NUMBER];
    int no = 0, res;
    void * thrd_ret;
    srand(time(NULL));
    for (no = 0; no < THREAD_NUMBER; no++) {
        sem_init(&sem[no], 0, 0);
        res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
        if (res != 0) {
            printf("Create thread %d failed\n", no);
            exit(res);
        }
    }

    printf("Create treads success\n Waiting for threads to finish...\n");
    sem_post(&sem[THREAD_NUMBER - 1]);
    for (no = THREAD_NUMBER - 1; no >= 0; no--) {
        res = pthread_join(thread[no], &thrd_ret);
        if (!res) {
            printf("Thread %d joined\n", no);
        } else {
            printf("Thread %d join failed\n", no);
        }
        sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);           
    }

    for (no = 0; no < THREAD_NUMBER; no++) {
        sem_destroy(&sem[no]);      
    }
    return 0;        
}

執行結果,仍然是建立3條線程,每條線程執行時休眠隨機時長:

$ gcc thread_sem.c -lpthread 
$ ./a.out 
Create treads success
 Waiting for threads to finish...
Thread 2 is starting
        Thread 2: job 0 delay = 9
        Thread 2: job 1 delay = 9
        Thread 2: job 2 delay = 5
Thread 2 finished
Thread 2 joined
Thread 1 is starting
        Thread 1: job 0 delay = 5
        Thread 1: job 1 delay = 7
        Thread 1: job 2 delay = 4
Thread 1 finished
Thread 1 joined
Thread 0 is starting
        Thread 0: job 0 delay = 3
        Thread 0: job 1 delay = 9
        Thread 0: job 2 delay = 8
Thread 0 finished
Thread 0 joined

執行結果與第2個例程非常相似,只不過教材中進行倒序執行而已; 
那么這種方式其實與使用互斥鎖相比,代碼量可讀性基本持平不相上下;

線程的基本屬性pthread_attr_setscope

設置屬性一般有: 
1 綁定屬性 
2 分離屬性 
3 堆棧地址 
4 堆棧大小 
5 優先級

關於綁定屬性就是綁定於內核線程; 
分離屬性主要是講線程結束后是否馬上釋放相應的內存;

/* thread_attr.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREAD_NUMBER       1
#define REPEAT_NUMBER       3
#define DELAY_TIME_LEVELS   10.0
int finish_flag = 0;

void * thrd_func(void * arg){
    int delay_time = 0;
    int count = 0;
    printf("Thread is starting\n");
    for (count = 0; count < REPEAT_NUMBER; count++) {
        delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
        sleep(delay_time);
        printf("\tThread : job %d delay = %d\n", count, delay_time);
    }
    printf("Thread finished\n");
    finish_flag = 1;
    pthread_exit(NULL);
}

int main(void) {
    pthread_t thread;
    pthread_attr_t attr;
    int res = 0;
    srand(time(NULL));
    res = pthread_attr_init(&attr);
    if (res != 0) {
        printf("Create attribute failed\n");
        exit(res);
    }
    res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (res != 0) {
        printf("Setting attribute failed\n");
        exit(res);
    }
    res = pthread_create(&thread, &attr, thrd_func, NULL);
    if (res != 0) {
        printf("Create thread failed\n");
        exit(res);
    }
    pthread_attr_destroy(&attr);
    printf("Create tread success\n");

    while(!finish_flag){
        printf("Waiting for thread to finish...\n");
        sleep(2);
    }
    return 0;        
}

在運行前后使用 $ free 命令查看內存前后的使用情況發現: 
在線程結束后內存馬上被釋放;
其實,一般線程的屬性直接使用系統默認屬性即可; 
關於線程的使用,大約就是這樣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM