歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux 多線程編程

Linux 多線程編程

日期:2017/3/1 9:11:14   编辑:Linux編程

概念

  • 原來指向main()的線程叫做主線程(main thread)
  • 使用pthread_create()創建出來的線程,叫做子線程(child thread)
  • 主/子線程只有在創建時才有區別, 創建完了就一視同仁, 都是一樣的獨立個體, 可以有交流、共享和私有, 但沒有上下級, 這一點和多進程一樣, 只有在創建的瞬間才有parent process 和child process 的區別, 創建完了就都是一樣的獨立個體
  • 創建完子線程之後,兩個線程之間獨立運行,線程的執行先後次序由OS的調度算法決定
  • 線程之間相互獨立也相互影響,因為主線程結束時,會導致進程結束,進程結束時,會導致該進程的所有線程結束
  • 多個線程共享一個進程, 而一個進程只有一個輸出終端, So一定要調度好, 要不有的線程輸出會看不到, 最low的做法就是sleep()一下保證線程可以執行完

模型

  1. 獲得ThreadID :pthread_self()/pthread_equal()
  • 創建一個線程前設置 :pthread_attr_init()/pthread_attr_setdetachstate()/…
  • 創建一個線程 :pthread_create()
  • 已有一個線程後設置 :pthread_detach()pthread_setcancelstate()/pthread_setcanceltype()
  • 向線程發送一個信號 :pthread_kill()
  • 退出線程但不退出進程 :pthread_exit()
  • 終止另一個線程 :pthread_cancel()
  • 等待一個線程 :pthread_join()

  • $gcc -pthread

頭文件

#include<pthread.h>

pthread_self()

//返回調用線程的ID
pthread_t pthread_self(void);

pthread_equal()

//對比兩個線程ID,相等返回非0,不等返回0
int pthread_equal(pthread_t t1, pthread_t t2);

pthread_attr_init()/ pthread_attr_destroy()

//pthread_attr_init()初始化一個線程屬性對象attr,不指定attr就按默認參數進行初始化。
//pthread_attr_destroy()銷毀一個線程屬性對象,銷毀的操作對使用這個attr的線程有影響
//成功返回0,失敗返回error number
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

更改attr對象的函數

//成功返回0,失敗返回error number
typedef struct{
    int                 detachstate;        //線程的分離狀態       
    int                 schedpolicy;        //線程調度策略
    struct sched_param  schedparam;         //線程的調度參數
    int                 inheritsched;       //線程的繼承性
    int                 scope;              //線程的作用域
    size_t              guardsize;          //線程棧末尾的警戒緩沖區大小
    int                 stackaddr_set;      //線程的棧設置
    void *              stackaddr;          //線程棧的位置
    size_t              stacksize;          //線程棧的大小
}pthread_attr_t;

pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate)
pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate)
PTHREAD_CREATE_JOINABLE /   PTHREAD_CREAT_DETACHED
int detachstate;

pthread_attr_getdetachstate(&attr,&detachstate);
if(PTHREAD_CREATE_JOINABLE==detachstate)
    printf("1.PTHREAD_CREATE_JOINABLE\n");      //default

pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
SCHED_OTHER     /   SCHED_FIFO  /   SCHED_RR

pthread_attr_setsschedchedparam(pthread_attr_t *attr, const struct sched_param *param);
pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
struct sched_param {
    int sched_priority;// Scheduling priority,int sched_get_priority_max/sched_get_priority_min (int policy)
};

pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
PTHREAD_INHERIT_SCHED       /    PTHREAD_EXPLICIT_SCHED

pthread_attr_setscope(pthread_attr_t *attr, int scope);
pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
PTHREAD_SCOPE_SYSTEM        /   PTHREAD_SCOPE_PROCESS

pthread_attr_setguardsize ( pthread_attr_t *attr, size_t guardsize );
pthread_attr_getguardsize ( const pthread_attr_t *attr, size_t *guardsize );
>0              /   0
(默認1 page,當然還可以指定任意值)

pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);

pthread_attr_setstacksize ( pthread_attr_t *attr, size_t size );
pthread_attr_getstacksize ( const pthread_attr_t *attr, size_t *size );

pthread_create()

//這個函數的create有‘e’   //p代表POSIX
//在調用進程中創建一個新的線程,新的線程通過激活start_routine()來開始執行,arg是start_routine()唯一的參數
//成功返回0,失敗返回error number
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

thread:存放新線程的ID到該參數所指向的緩沖區中, pthread_t是unsigned long int
attr: 線程的屬性, 給NULL表示默認方式
start_routine:指定新線程的處理函數,也就是新線程啟動之後需要執行的代碼,線程處理函數執行完畢之後, 新線程終止
arg: 用於給start_routine傳遞實參(VS on_exit())

pthread_detach()

//將thread表示的線程標記為detached,當一個detached的線程結束後,它占用的資源被自動釋放,其他線程也不能用pthread_join()等待
//detach一個已經被detach了的線程將導致不確定的結果
//成功返回0,失敗返回error number

int pthread_detach(pthread_t thread);

pthread_setcancelstate()

//設置當前線程是否允許被cancel,成功返回0,失敗返回error number                  
int pthread_setcancelstate(int state, int *oldstate);

state:設置新狀態

  • PTHREAD_CANCEL_ENABLE //允許取消
  • PTHREAD_CANCEL_DISABLE //不允許取消\

oldstate:用於帶出設置之前的舊狀態

pthread_setcanceltype()

//設置可取消性的類型,即當前線程何時被取消  
//成功返回0,失敗返回error number                    
int pthread_setcanceltype(int type, int *oldtype);

type:設置新類型

  • PTHREAD_CANCEL_DEFERED //延遲取消
  • PTHREAD_CANCEL_ASYNCHRONOUS //立即取消

oldtype:用於帶出設置之前的舊類型

pthread_exit()

//結束調用線程並返回一個retval值,這個值可以被同進程中的其他線程通過pthread_join()來讀取,當然,前提條件是當前線程可以被匯合/等待
void pthread_exit(void *retval);

pthread_cancel()

//發送一個結束請求給一個線程,是否取消以及何時取消取決於線程的屬性:state和type
//成功返回0,失敗返回error number
int pthread_cancel(pthread_t thread);

進程的7種終止方式:

正常終止:

  1. 從 main 返回
  • 調用 exit() / _exit() / _Exit()
  • 最後一個線程從其啟動例程返回
  • 最後一個線程調用pthread_exit

異常終止:

  1. 調用abort()
  • 接到一個信號並終止
  • 最後一個線程對取消請求作出響應

pthread_join()

//等待thread指定的線程終止,如果目標線程沒有終止,則當前線程進入阻塞狀態,當目標線程終止時,該函數立即返回,前提:目標線程可以被匯合/等待
//如果調用pthread_join()的線程被cancel了,目標線程互保持joinable(不會被撤銷)
//如果多個線程同時join一個線程,那麼結果是不確定的
//成功返回0,失敗返回error number
int pthread_join(pthread_t thread, void **retval);

thread:線程編號
retval:二級指針的返回值
retval

  • 不是NULL,將拷貝目標線程的狀態信息到*retval
  • 如果目標線程被cancel了,則將PTHREAD_CANCELED防止在*retval

例子

//02join.c, 使用pthread_join等待目標線程結束
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>      //strerror()//沒有這個.h會把strerror(error)當int

void* Calc(void* pv){
    printf("primeter is %lg, area is:%lg\n",2*3.14**(int*)pv,3.14**(int*)pv**(int*)pv);
    return NULL;    //要求有返回值,所以返回NULL
}
main(){
    int* piRadius=(int*)malloc(sizeof(int));
    printf("please input radius\n");
    scanf("%d",piRadius);
    pthread_t thread;
    int error=pthread_create(&thread,NULL,Calc,(void*)piRadius);
    if(0!=error)
        printf("%s\n",strerror(error)),exit(-1);
    error=pthread_join(thread,NULL);
    if(0!=error)
        printf("%s\n",strerror(error)),exit(-1);
    free(piRadius);
    piRadius=NULL;
    return 0;
} 
//03join.c  使用pthread_join獲取目標線程的退出狀態
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
void* task(void* pv){
    char* pc="hello";
    return (void*)pc;
}
main(){
    //創建子線程,使用pthread_create
    pthread_t thread;
    int error=pthread_create(&thread,NULL,task,NULL);
    if(0!=error)
        printf("%s",strerror(error)),exit(-1);
    //等待子線程結束,並獲取退出狀態信息
    char* ps=NULL;
    error=pthread_join(thread,(void**)&ps);
    if(0!=error)
        printf("pthread_join %s\n",strerror(error)),exit(-1);
    printf("child thread returned:%s\n",ps);
    return 0;
} 
//使用pthread_create()創建新線程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

void* task(void* pv){       //需要一個實參,我們又不需要,就可以給個NULL
    //判斷thread中的線程ID是否與pthread_self()的線程ID相等
    int res=pthread_equal(*(pthread_t*)pv,pthread_self());
    if(0!=res)
        printf("these two are Equal\n");
    else
        printf("these two are Unequal\n");
    int i=0;
    for(i=0;i<10;i++){
        printf("I am the new thread\n");
        sleep(1);
    }
}
int main(){
    //1.准備存儲線程編號的變量
    pthread_t thread;

    //2.創建新線程,使用pthread_create()
    int errno=pthread_create(&thread,NULL,task,(void*)&thread);//子線程執行完task()就結束了,使用arg把thread傳入子線程
    if(0!=errno)
        printf("pthread_create:%s\n",strerror(errno)),exit(-1);
    sleep(10);
    printf("child thread's ID:%lu,parent thread's ID:%lu\n",thread,pthread_self());//pthread_self(),獲取當前線程自己的線程編號
    return 0;
}
//使用pthread_create()創建子線程,在線程處理函數中計算1~100之間的和,保存在sum中,返回該變量的地址,主線程等待子線程結束,並獲取退出狀態信息,並打印
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
void* Sum(void* pv){
    int i=0,sum=0;
    for(i=1;i<=100;i++)
        sum+=i;
    printf("%d\n",sum);
    return (void*)"success";    //其實return "success"就行
}
int main(){
    pthread_t thread;
    int res=pthread_create(&thread,NULL,Sum,NULL);
    if(0!=res)
        printf("%s",strerror(res)),exit(-1);
    char *retval=NULL;
    res=pthread_join(thread,(void**)&retval);
    if(0!=res)
        printf("%s\n",strerror(res)),exit(-1);
    printf("child thread return : %s\n",retval);
    return 0;
}
//在線程處理函數中打印1~20之間的函數,當打印到10時終止當前進程並帶出10,主線程等待並獲取退出狀態信息,打印最終結果
#include<stdio.h>
#include<pthread.h>
void* task(void* pv){
    static int i=0;
    for(i=1;i<=20;i++){
        if(10==i)
            pthread_exit((void*)&i);    //pthread_exit()  VS  exit()
        else
            printf("i=%d\n",i);
    }
    return NULL;
}
main(){
    pthread_t thread;
    pthread_create(&thread,NULL,task,NULL);
    int *retval=NULL;
    pthread_join(thread,(void**)&retval);       //沒有錯誤處理
    printf("task returned:%d\n",*retval);
    return 0;
}
//使用pthread_cancel()取消指定的線程
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void* task(void* pv){
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);        //設置該線程不可取消
    while(1){
        printf("I am superman\n");
        sleep(1);
    }
    return NULL;
}
void* task2(void* pv){
    printf("Cancelling thread...\n");
    sleep(2);
    pthread_cancel(*(pthread_t*)pv);
    printf("cancelling successfully\n");
}
main(){
    pthread_t thread;
    pthread_t thread2;
    pthread_create(&thread,NULL,task,NULL);
    pthread_create(&thread2,NULL,task2,(void*)&thread); 
    pthread_join(thread,NULL);
    pthread_join(thread2,NULL);
    return 0;
}
/*
I am superman
I am superman
Cancelling thread...
I am superman
cancelling successfully
I am superman       //取消失敗了, 說明設置禁止取消成功了
I am superman
*/

Copyright © Linux教程網 All Rights Reserved