前面介紹了pthread_create函數,並且當時的例子中,傳入的參數都是空指針,而不是指向pthread_attr_t結構的指針。可以使用pthread_attr_t結構修改線程默認屬性,並把這些屬性與創建的線程聯系起來。可以使用pthread_attr_init函數初始化pthread_attr_t結構。調用pthread_attr_init以后,pthread_attr_t結構所包含的內容就是操作系統實現支持的線程所有屬性的默認值。如果要修改其中個別屬性的值,需要調用其他的函數。
#include <pthread.h>
int pthread_attr_init( pthread_attr_t *attr );
int pthread_attr_destroy( pthtread_attr_t *attr );
兩個函數的返回值都是:若成功則返回0,否則返回錯誤編號
如果要去除對pthread_attr_t結構的初始化,可以調用pthread_attr_destroy函數。如果pthread_attr_init實現時為屬性對象分配了動態內存空間,pthread_attr_destroy將會釋放該內存空間。除此之外,pthread_attr_destroy還會用無效的值初始化屬性對象,因此如果該屬性對象被誤用,將會導致pthread_create函數返回錯誤。
pthread_attr_t結構對應用程序是透明的,也就是說應用程序並不需要了解有關屬性對象內部結構的任何細節,因而可以增強應用程序的可移植性。POSIX.1沿用了這種模型,並且為查詢和設置每種屬性定義了獨立的函數
名稱 |
描述 |
detachstate |
線程的分離狀態屬性 |
guardsize |
線程棧末尾的警戒緩沖區大小(字節數) |
stackaddr |
線程棧的最低地址 |
stacksize |
線程棧的大小(字節數) |
如果對現有的某個線程的終止狀態不感興趣的話,可以使用pthread_detach函數讓操作系統在線程退出時收回它所占用的資源。
如果在創建線程時就知道不需要了解線程的終止狀態,則可以修改pthread_attr_t結構中的detachstate線程屬性,讓線程以分離狀態啟動。可以使用pthread_attr_setdetachstate函數把線程屬性detachstate設置為下面的兩個合法值之一:設置為PTHREAD_CREATE_DETACHED,以分離狀態啟動線程;或者設置為PTHREAD_CREATE_JOINABLE,正常啟動線程,應用程序可以獲取線程的終止狀態。
#include <pthread.h>
int pthread_attr_getdetachstate( const pthread_attr_t *restrict attr,
int *detachstate );
int pthread_attr_setdetachstate( pthread_attr_t *attr, int detachstate );
兩者的返回值都是:若成功則返回0,否則返回錯誤編號
可以調用pthread_attr_getdetachstate函數獲取當前的detachstate線程屬性,第二個參數所指向的整數用來保存獲取到的detachstate屬性值:PTHREAD_CREATE_DETACHED,或PTHREAD_CREATE_JOINABLE)。
實例
程序:以分離狀態創建的線程
復制代碼
#include "apue.h"
#include <pthread.h>
int
makethread(void *(*fn)(void *), void *arg)
{
int err;
pthread_t tid;
pthread_attr_t attr;
err = pthread_attr_init(&attr);
if(err != 0)
return(err);
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(err == 0)
err = pthread_create(&tid, &attr, fn, arg);
pthread_attr_destroy(&attr);
return(err);
}
這里忽略了pthread_attr_destroy函數調用的返回值。在這種情況下,由於對線程屬性進行了合理的初始化,pthread_attr_destroy一般不會失敗。但是如果pthread_attr_destroy確實出現了失敗的情況,清理工作就會變得很困難:必須銷毀剛剛創建的線程,而這個線程可能已經運行,並且與pthread_attr_destroy函數可能是異步執行的。忽略pthread_attr_destroy的錯誤返回可能出現的最壞的情況是:如果pthread_attr_init分配了內存空間,這些內存空間會被泄露。另一方面,如果pthread_attr_init成功地對線程屬性進行了初始化,但pthread_attr_destroy在做清理工作時卻出現了失敗,就沒有任何補救策略,因為線程屬性結構對應用程序來說是透明的,可以對線程屬性結構進行清理的唯一接口是pthread_attr_destroy,但它失敗了。
對於遵循POSIX標准的操作系統來說,並不一定要支持線程棧屬性,但是對於遵循XSI的系統,支持線程棧屬性就是必須的。可以在編譯階段使用_POSIX_THREAD_ATTR_STACKADDR和_POSIX_ATTR_STACKSIZE符號來檢查系統是否支持線程棧屬性,如果系統定義了這些符號,就說明它支持相應的線程棧屬性。也可以通過在運行階段把_SC_THREAD_ATTR_STACKADDR和_SC_THREAD_ATTR_STACKSIZE參數傳給sysconf函數,檢查系統對線程棧屬性的支持情況。
POSIX.1定義了線程棧屬性的一些操作接口。線程棧屬性的查詢和修改一般是通過較新的函數pthread_attr_getstack和pthread_attr_setstack來進行。
#include <pthread.h>
int pthread_attr_getstack( const pthread_attr_t *restrict attr,
void **restrict stackaddr,
size_t *restrict stacksize );
int pthread_attr_setstack( const pthread_attr_t *attr,
void *stackaddr, size_t *stacksize );
兩者的返回值都是:若成功則返回0,否則返回錯誤編號
如果希望改變棧的默認大小,但又不想自己處理線程棧的分配問題,這時使用pthread_attr_setstacksize函數就非常有用。
線程屬性guardsize控制着線程棧末尾之后用以避免棧溢出的擴展內存的大小。這個屬性默認設置為PAGESIZE個字節。可以把guardsize線程屬性設為0,從而不允許屬性的這種特征行為發生:在這種情況下不會提供警戒緩沖區。同樣地,如果對線程屬性stackaddr作了修改,系統就會假設我們會自己管理棧,並使警戒棧緩沖區機制無效,等同於把guardsize屬性設為0。
復制代碼
#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,否則返回錯誤編號
如果guardsize線程屬性被修改了,操作系統可能把它取為頁大小的整數倍。如果線程的棧指針溢出到警戒區域,應用程序就可能通過信號接收到出錯信息。