天天看點

Linux系統程式設計----15(線程與程序函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

對比

程序 				線程
fork 				pthread_create
exit (10)			pthread_exit (void *)
wait (int *)		pthread_join (,void **)阻塞
kill 				pthread_cancel ();必須到取消點(檢查點):系統調用   man 7 pthreads
getpid 				pthread_self 				命名空間
					pthread_detach:分離,好處:自動清理pcb
           

什麼類型作為傳回值,回收就用哪個類型的位址

線程屬性

linux 下線程的屬性是可以根據實際項目需要,進行設定,之前我們讨論的線程都是采用 線程的預設屬性,預設屬性已經可以解決絕大多數開發時遇到的問題。

如我們對程式的性能提出更高的要求那麼需 要設定線程屬性,比如可以通過設定線程棧的大小來降低記憶體的使用,增加最大線程個數。

Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

主要結構體成員:

  1. 線程分離狀态
  2. 線程棧大小(預設8M平均配置設定)
  3. 線程棧警戒緩沖區大小(位于棧末尾) 為了防止線程溢出

線程屬性說明

  1. 屬性值不能直接設定,須使用相關函數進行操作,初始化的函數為

    pthread_attr_init

    ,這個函數必須在

    pthread_create

    函數之前調用。之後須用

    pthread_attr_destroy

    函數來釋放資源。
  2. 線程屬性主要包括如下屬性:**作用域(scope)、棧尺寸(stacksize)、棧位址(stackaddress)、優先級(priority)、 分離的狀态(detachedstate)、排程政策和參數(schedulingpolicyandparameters)。**預設的屬性為非綁定、非分離、 預設的堆棧、與父程序同樣級别的優先級。

線程屬性控制流程

線程屬性初始化

注意:應先初始化線程屬性,再 pthread_create 建立線程

  1. 初始化線程屬性

    int pthread_attr_init(pthread_attr_t *attr);

    成功:0;失敗:錯誤号
  2. 銷毀線程屬性所占用的資源

    int pthread_attr_destroy(pthread_attr_t *attr);

    成功:0;失敗:錯誤号

線程的分離狀态

線程的分離狀态決定一個線程以什麼樣的方式來終止自己。

  1. 非分離狀态:線程的預設屬性是非分離狀态,這種情況下,原有的線程等待建立的線程結束。隻有當 pthread_join() 函數傳回時,建立的線程才算終止,才能釋放自己占用的系統資源。
  2. 分離狀态:分離線程沒有被其他的線程所等待,自己運作結束了,線程也就終止了,馬上釋放系統資源。應該 根據自己的需要,選擇适當的分離狀态。

線程分離狀态的函數:

設定線程屬性,分離 or 非分離

int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);

擷取線程屬性,分離 or 非分離

int pthread_attr_getdetachstate (pthread_attr_t *attr,int *detachstate);

參數: attr:已初始化的線程屬性
		 detachstate: 
					  PTHREAD_CREATE_DETACHED(分離線程) 		
					  PTHREAD_CREATE_JOINABLE(非分離線程) 
           

示例1:

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>


void *thrd_func(void *arg)
{
    pthread_exit((void *)77);
}

int main(void)
{
    pthread_t tid;
    int ret;
    pthread_attr_t attr;

    ret = pthread_attr_init(&attr);
    if(ret != 0){ 
        fprintf(stderr,"pthread_init error: %s\n",strerror(ret));
        exit(1);
    }   

    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&tid,&attr,thrd_func,NULL);                         

    if(ret != 0){ 
        fprintf(stderr,"pthread_create error: %s\n",strerror(ret));
        exit(1);
    }   
    ret = pthread_join(tid,NULL);
    
    printf("---------join ret = %d\n",ret);
    pthread_exit((void *)1);
}
           
Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

示例2

#include<stdio.h>                                                            
#include<unistd.h>
#include<string.h>
#include<pthread.h>

void *tfn(void *arg)
{
    int n=3;
    
    while(n--){
        printf("thread count %d\n",n);
        sleep(1);
    }

    return (void *)1;
}

int main(void)
{
    pthread_t tid;
    void *tret;
    int err;
 
#if 1
    pthread_attr_t attr;  //通過線程屬性類設定遊離态
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid,&attr,tfn,NULL);

#else
    pthread_create(&tid,NULL,tfn,NULL);
    pthread_detach(tid);        //讓線程分離   ----自動退出,無系統殘留資源
#endif
    while(1){
        err = pthread_join(tid,&tret);
        printf("---------err= %d\n",err);
        if(err != 0)
            fprintf(stderr,"thread error: %s\n",strerror(err));
    else
            fprintf(stderr,"thread exit code %d\n",(int)tret);

        sleep(1);
    }
    return 0;
}              
           
Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

注意事項

  1. 如果設定一個線程為分離線程,而這個線程運作又非常快,它很可能在 pthread_create 函數傳回之前就終止了,它終止以後就可能将線程号和系統資源移交給其他的線程使用,這樣調用 pthread_create 的線程就得到了錯誤的線程号。
  2. 要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被建立的線 程裡調用 pthread_cond_timedwait 函數,讓這個線程等待一會兒,留出足夠的時間讓函數 pthread_create 傳回。
  3. 設 置一段等待時間,是在多線程程式設計裡常用的方法。但是注意不要使用諸如 wait()之類的函數,它們是使整個程序睡

    眠,并不能解決線程同步的問題。

線程的棧大小

當系統中有很多線程時,可能需要減小每個線程棧的預設大小,防止程序的位址空間不夠用,當線程調用的函 數會配置設定很大的局部變量或者函數調用層次很深時,可能需要增大線程棧的預設大小。

函數

pthread_attr_getstacksize

pthread_attr_setstacksize

提供設定。

  1. int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);

    成功:0;失敗:錯誤号
  2. int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);

    成功:0;失敗:錯誤号
  3. 參數:

    attr:指向一個線程屬性的指針 stacksize:傳回線程的堆棧大小

Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

預設線程棧均分8M空間

檢視一個程序最多能建立多少個線程

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>

void *tfn(void *arg)
{
    while(1)
        sleep(1);
}

int main(void)
{
    pthread_t tid;
    int ret,count = 1;
    for(;;){
        ret = pthread_create(&tid,NULL,tfn,NULL);
        if(ret != 0){ 
            printf("%s\n",strerror(ret));                                    
            break;
        }   
        printf("-----------%d\n",++count);
    }   
    return 0;
}
           
Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

我的是能建立4094個線程

線程屬性控制示例

#include<stdio.h>                                                            
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>

#define SIZE 0x100000

void *th_fun(void * arg)
{
    while(1);
    sleep(1);
}

int main(void)
{
    pthread_t tid;
    int err, detachstate,i=1;
    pthread_attr_t attr;
    size_t stacksize;
    void *stackaddr;

    pthread_attr_init(&attr);
    pthread_attr_getstack(&attr,&stackaddr,&stacksize);
    pthread_attr_getdetachstate(&attr,&detachstate);

    if(detachstate == PTHREAD_CREATE_DETACHED)//預設是分離态
        printf("thread detached\n");
    else if(detachstate == PTHREAD_CREATE_JOINABLE)//預設非分離
        printf("thread join\n");
    else
        printf("thread un known\n");

    //設定線程分離
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

    while(1){
        //在堆上申請記憶體,指定線程棧的起始位址和大小
        stackaddr = malloc(SIZE);
            if(stackaddr == NULL){
            perror("malloc");
            exit(1);
        }
        stacksize = SIZE;
        pthread_attr_setstack(&attr,stackaddr,stacksize);//借助線程的屬性,修
改線程棧空間大小

        err = pthread_create(&tid,&attr,th_fun,NULL);
        if(err != 0){
            printf("%s\n",strerror(err));
            exit(1);
        }
        printf("%d\n",i++);//i表示循環建立多少次
    }

    pthread_attr_destroy(&attr);

    return 0;
}       
           
Linux系統程式設計----15(線程與程式函數之間的對比,線程屬性及其函數,線程屬性控制流程,線程使用注意事項,線程庫)對比線程屬性線程屬性控制流程線程使用注意事項

NPTL(線程庫)

  1. 檢視目前 pthread 庫版本 getconfGNU_LIBPTHREAD_VERSION
  2. NPTL 實作機制(POSIX),NativePOSIXThreadLibrary
  3. 使用線程庫時 gcc 指定 –lpthread

線程使用注意事項

  1. 主線程退出其他線程不退出,主線程應調用 pthread_exit
  2. 避免僵屍線程

    pthread_join

    pthread_detach

    pthread_create 指定分離屬性

    被 join 線程可能在 join 函數傳回前就釋放完自己的所有記憶體資源,是以不應當傳回被回收線程棧中的值;

  3. malloc 和 mmap 申請的記憶體可以被其他線程釋放
  4. 應避免在多線程模型中調用 fork 除非,馬上 exec,子程序中隻有調用 fork 的線程存在,其他線程在子程序 中均 pthread_exit
  5. 信号的複雜語義很難和多線程共存,應避免在多線程引入信号機制

繼續閱讀