1. 程式人生 > >Posix多執行緒程式設計—執行緒屬性

Posix多執行緒程式設計—執行緒屬性

一.執行緒屬性
執行緒具有屬性,用pthread_attr_t表示,在對該結構進行處理之前必須進行初始化,在使用後需要對其去除初始化。我們用pthread_attr_init函式對其初始化,用pthread_attr_destroy對其去除初始化。
1.名稱:pthread_attr_init/pthread_attr_destroy
功能:對執行緒屬性初始化/去除初始化
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_init(pthread_attr_t *attr);
              int pthread_attr_destroy(pthread_attr_t *attr);
引數:Attr 執行緒屬性變數
返回值:若成功返回0,若失敗返回-1。
    呼叫pthread_attr_init之後,pthread_t結構所包含的內容就是作業系統實現支援的執行緒所有屬性的預設值。
    如果要去除對pthread_attr_t結構的初始化,可以呼叫pthread_attr_destroy函式。如果pthread_attr_init實現時為屬性物件分配了動態記憶體空間,pthread_attr_destroy還會用無效的值初始化屬性物件

,因此如果經pthread_attr_destroy去除初始化之後的pthread_attr_t結構被pthread_create函式呼叫,將會導致其返回錯誤。
    執行緒屬性結構如下:
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_join()函式返回時,建立的執行緒才算終止,才能釋放自己佔用的系統資源。
    而分離執行緒不是這樣子的,它沒有被其他的執行緒所等待,自己執行結束了,執行緒也就終止了,馬上釋放系統資源。程式設計師應該根據自己的需要,選擇適當的分離狀態。所以如果我們在建立執行緒時就知道不需要了解執行緒的終止狀態,則可以pthread_attr_t結構中的detachstate執行緒屬性,讓執行緒以分離狀態啟動。
2.名稱:pthread_attr_getdetachstate/pthread_attr_setdetachstate
功能:獲取/修改執行緒的分離狀態屬性
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getdetachstate(const pthread_attr_t * attr,int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
引數:Attr 執行緒屬性變數, Detachstate 執行緒的分離狀態屬性
返回值:若成功返回0,若失敗返回-1。
    可以使用pthread_attr_setdetachstate函式把執行緒屬性detachstate設定為下面的兩個合法值之一:設定為PTHREAD_CREATE_DETACHED
,以分離狀態啟動執行緒;或者設定為PTHREAD_CREATE_JOINABLE,正常啟動執行緒。可以使用pthread_attr_getdetachstate函式獲取當前的datachstate執行緒屬性。
(1) 以分離狀態建立執行緒
#include <pthread.h>
void *child_thread(void *arg)
{
printf(“child thread run!\n”);
}
int main(int argc,char *argv[ ])
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&attr,fn,arg);
pthread_attr_destroy(&attr);
sleep(1);
}
三、執行緒的繼承性
    函式pthread_attr_setinheritsched和pthread_attr_getinheritsched分別用來設定和得到執行緒的繼承性,這兩個函式的定義如下:
3.名稱:pthread_attr_getinheritsched /pthread_attr_setinheritsched
功能:獲得/設定執行緒的繼承性
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
              int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);
引數:attr 執行緒屬性變數, inheritsched 執行緒的繼承性
返回值:若成功返回0,若失敗返回-1。
    這兩個函式具有兩個引數,第1個是指向屬性物件的指標,第2個是繼承性或指向繼承性的指標。繼承性決定排程的引數是從建立的程序中繼承還是使用在schedpolicy和schedparam屬性中顯式設定的排程資訊。Pthreads不為inheritsched指定預設值,因此如果你關心執行緒的排程策略和引數,必須先設定該屬性。
    繼承性的可能值是PTHREAD_INHERIT_SCHED(表示新現成將繼承建立執行緒的排程策略和引數)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam屬性中顯式設定的排程策略和引數)。如果你需要顯式的設定一個執行緒的排程策略或引數,那麼你必須在設定之前將inheritsched屬性設定為PTHREAD_EXPLICIT_SCHED.

四、執行緒的排程策略
     函式pthread_attr_setschedpolicy和pthread_attr_getschedpolicy分別用來設定和得到執行緒的排程策略。
4.名稱:pthread_attr_getschedpolicy \pthread_attr_setschedpolicy
功能:獲得/設定執行緒的排程策略
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy);
              int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
引數:attr 執行緒屬性變數, policy 排程策略
返回值:若成功返回0,若失敗返回-1。
     這兩個函式具有兩個引數,第1個引數是指向屬性物件的指標,第2個引數是排程策略或指向排程策略的指標。排程策略可能的值是先進先出(SCHED_FIFO)、輪轉法(SCHED_RR),或其它(SCHED_OTHER)。
   (1) SCHED_FIFO策略允許一個執行緒執行直到有更高優先順序的執行緒準備好,或者直到它自願阻塞自己。在SCHED_FIFO排程策略下,當有一個執行緒準備好時,除非有平等或更高優先順序的執行緒已經在執行,否則它會很快開始執行。
   (2) SCHED_RR(輪循)策略是基本相同的,不同之處在於:如果有一個SCHED_RR策略的執行緒執行了超過一個固定的時期(時間片間隔)沒有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同優先順序的執行緒準備好時,執行的執行緒將被搶佔以便準備好的執行緒可以執行。
    當有SCHED_FIFO或SCHED_RR策賂的執行緒在一個條件變數上等持或等持加鎖同一個互斥量時,它們將以優先順序順序被喚醒。即,如果一個低優先順序的  SCHED_FIFO執行緒和一個高優先織的SCHED_FIFO執行緒都在等待鎖相同的互斥且,則當互斥量被解鎖時,高優先順序執行緒將總是被首先解除阻塞。
五、執行緒的排程引數
    函式pthread_attr_getschedparam 和pthread_attr_setschedparam分別用來設定和得到執行緒的排程引數。
5.名稱:pthread_attr_getschedparam \pthread_attr_setschedparam
功能:獲得/設定執行緒的排程引數
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
引數:attr 執行緒屬性變數, param sched_param結構
返回值:若成功返回0,若失敗返回-1。
    這兩個函式具有兩個引數,第1個引數是指向屬性物件的指標,第2個引數是sched_param結構或指向該結構的指標。結構sched_param在檔案/usr/include /bits/sched.h中定義如下:
struct sched_param
{
int sched_priority;
};
    結構sched_param的子成員sched_priority控制一個優先權值,大的優先權值對應高的優先權。系統支援的最大和最小優先權值可以用sched_get_priority_max函式和sched_get_priority_min函式分別得到。
注意:如果不是編寫實時程式,不建議修改執行緒的優先順序。因為,排程策略是一件非常複雜的事情,如果不正確使用會導致程式錯誤,從而導致死鎖等問題。如:在多執行緒應用程式中為執行緒設定不同的優先級別,有可能因為共享資源而導致優先順序倒置。
6.名稱:sched_get_priority_max \sched_get_priority_min
功能:獲得系統支援的執行緒優先權的最大和最小值
標頭檔案:#include <pthread.h>
函式原形:int sched_get_priority_max(int policy); int sched_get_priority_min(int policy);
引數:policy 系統支援的執行緒優先權的最大和最小值
返回值:若成功返回0,若失敗返回-1。
    下面是上面幾個函式的程式例子:
#include <pthread.h>
#include <sched.h>
void *child_thread(void *arg)
{
int policy;
int max_priority,min_priority;
struct sched_param param;
pthread_attr_t attr;
pthread_attr_init(&attr); /*初始化執行緒屬性變數*/
pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED); /*設定執行緒繼承性*/
pthread_attr_getinheritsched(&attr,&policy); /*獲得執行緒的繼承性*/
if(policy==PTHREAD_EXPLICIT_SCHED)
printf(“Inheritsched:PTHREAD_EXPLICIT_SCHED\n”);
if(policy==PTHREAD_INHERIT_SCHED)
printf(“Inheritsched:PTHREAD_INHERIT_SCHED\n”);
pthread_attr_setschedpolicy(&attr,SCHED_RR);/*設定執行緒排程策略*/
pthread_attr_getschedpolicy(&attr,&policy);/*取得執行緒的排程策略*/
if(policy==SCHED_FIFO)
printf(“Schedpolicy:SCHED_FIFO\n”);
if(policy==SCHED_RR)
printf(“Schedpolicy:SCHED_RR\n”);
if(policy==SCHED_OTHER)
printf(“Schedpolicy:SCHED_OTHER\n”);
sched_get_priority_max(max_priority);/*獲得系統支援的執行緒優先權的最大值*/
sched_get_priority_min(min_priority);/* 獲得系統支援的執行緒優先權的最小值*/
printf(“Max priority:%u\n”,max_priority);
printf(“Min priority:%u\n”,min_priority);
param.sched_priority=max_priority;
pthread_attr_setschedparam(&attr,&param);/*設定執行緒的排程引數*/
printf(“sched_priority:%u\n”,param.sched_priority);/*獲得執行緒的排程引數*/
pthread_attr_destroy(&attr);
}
int main(int argc,char *argv[ ])
{
pthread_t child_thread_id;
pthread_create(&child_thread_id,NULL,child_thread,NULL);
pthread_join(child_thread_id,NULL);
}

六、執行緒的作用域
    函式pthread_attr_setscope和pthread_attr_getscope分別用來設定和得到執行緒的作用域,這兩個函式的定義如下:
7.名稱:pthread_attr_setscope\pthread_attr_getscope
功能:獲得/設定執行緒的作用域
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(const pthread_attr_t *attr,int *scope);
引數:attr 執行緒屬性變數, scope 執行緒的作用域
返回值:若成功返回0,若失敗返回-1。
    這兩個函式具有兩個引數,第1個是指向屬性物件的指標,第2個是作用域或指向作用域的指標,作用域控制執行緒是否在程序內或在系統級上競爭資源,可能的值是PTHREAD_SCOPE_PROCESS(程序內競爭資源),PTHREAD_SCOPE_SYSTEM.(系統級上競爭資源)。
七、執行緒堆疊的大小
    函式pthread_attr_setstacksize和pthread_attr_getstacksize分別用來設定和得到執行緒堆疊的大小,這兩個函式的定義如下所示:
8.名稱:pthread_attr_getdetstacksize\pthread_attr_setstacksize
功能:獲得/修改執行緒棧的大小
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr ,size_t *stacksize);
引數:attr 執行緒屬性變數,stacksize 堆疊大小  
返回值:若成功返回0,若失敗返回-1。
    這兩個引數具有兩個引數,第1個是指向屬性物件的指標,第2個是堆疊大小或指向堆疊大小的指標.如果希望改變棧的預設大小,但又不想自己處理執行緒棧的分配問題,這時使用pthread_attr_setstacksize函式就非常有用
八、執行緒堆疊的地址
函式pthread_attr_setstackaddr和pthread_attr_getstackaddr分別用來設定和得到執行緒堆疊的位置,這兩個函式的定義如下:
9.名稱:pthread_attr_setstackaddr\pthread_attr_getstackaddr
功能:獲得/修改執行緒棧的位置
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getstackaddr(const pthread_attr_t *attr,void **stackaddf);
int pthread_attr_setstackaddr(pthread_attr_t *attr,void *stackaddr);
引數:attr 執行緒屬性變數,stackaddr 堆疊地址
返回值:若成功返回0,若失敗返回-1。
這兩個函式具有兩個引數,第1個是指向屬性物件的指標,第2個是堆疊地址或指向堆疊地址的指標。
九、執行緒棧末尾的警戒緩衝區大小
函式pthread_attr_getguardsize和pthread_attr_setguardsize分別用來設定和得到執行緒棧末尾的警戒緩衝區大小,這兩個函式的定義如下:
10.名稱:pthread_attr_getguardsize/pthread_attr_setguardsize
功能:獲得/修改執行緒棧末尾的警戒緩衝區大小
標頭檔案:#include <pthread.h>
函式原形:int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr ,size_t *guardsize);
引數:
返回值:若成功返回0,若失敗返回-1。
    執行緒屬性guardsize控制著執行緒棧末尾之後以避免棧溢位的擴充套件記憶體大小。這個屬性預設設定為PAGESIZE個位元組。可以把guardsize執行緒屬性設為0,從而不允許屬性的這種特徵行為發生:在這種情況下不會提供警戒快取區。同樣地,如果對執行緒屬性stackaddr作了修改,系統就會假設我們會自己管理棧,並使警戒棧緩衝區機制無效,等同於把guardsize執行緒屬性設為0。