Linux系統編程 —線程屬性
- 2020 年 10 月 3 日
- 筆記
在之前的章節中,我們在調用pthread_create函數創建線程時,第二個參數(即線程屬性)都是設為NULL,即使用默認屬性。一般情況下,使用默認屬性已經可以解決我們開發過程中的大多數問題。
但是,有時項目中我們對線程會有些特殊的要求,比如修改線程棧的大小,直接調用線程的庫函數無法滿足需求,在這種情況下我們可以直接對線程屬性進行設置。
類型pthread_attr_t是一個結構體,主要包括如下屬性:作用域(scope)、棧尺寸(stack size)、棧地址(stack address)、優先級(priority)、分離的狀態(detached state)、調度策略和參數(scheduling policy and parameters)。
線程默認的屬性為非綁定、非分離、缺省的堆棧、與父進程同樣級別的優先級。結構體具體定義如下:
typedef struct
{
int etachstate; //線程的分離狀態
int schedpolicy; //線程調度策略
struct sched_param schedparam; //線程的調度參數
int inheritsched; //線程的繼承性
int cope; //線程的作用域
size_t guardsize; //線程棧末尾的警戒緩衝區大小
int stackaddr_set; //線程的棧設置
size_t stacksize; //線程棧的大小
} pthread_attr_t;
主要結構體成員:
1. 線程分離狀態:etachstate
2. 線程棧大小(默認平均分配):stacksize
3. 線程棧警戒緩衝區大小(位於棧末尾):guardsize
線程的屬性值不能直接設置,須使用相關函數進行操作。屬性的初始化的函數為pthread_attr_init,這個函數必須在pthread_create函數之前調用。使用完畢之後需調用pthread_attr_destroy函數來釋放資源。
線程屬性初始化
函數原型:
int pthread_attr_init(pthread_attr_t *attr);
返回值:
成功:0;失敗:錯誤號。
函數作用:
初始化線程屬性;
注意:應先初始化線程屬性,再調用pthread_create創建線程。
線程屬性銷毀
函數原型:
int pthread_attr_destroy(pthread_attr_t *attr);
返回值:
成功:0;失敗:錯誤號
函數作用:
銷毀線程屬性所佔用的資源
線程的分離狀態
線程的分離狀態決定一個線程最後終止的時候是以怎樣的方式回收資源。
非分離狀態:線程的默認屬性是非分離狀態,這種情況下,線程運行結束後,只有當其它線程調用pthread_join()函數去回收它時,創建的線程才算終止,才能釋放自己佔用的系統資源。
分離狀態:線程如果設置為分離狀態,則它將主動與主控線程脫離關係,當它自己運行結束了,線程也就終止了,馬上釋放系統資源。
設置線程分離狀態的函數:
設置線程屬性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
獲取程屬性
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
參數:attr:指向一個線程屬性的指針
detachstate:線程分離狀態
PTHREAD_CREATE_DETACHED(分離線程)
PTHREAD _CREATE_JOINABLE(非分離線程)
線程的棧地址
POSIX.1定義了兩個常量_POSIX_THREAD_ATTR_STACKADDR 和_POSIX_THREAD_ATTR_STACKSIZE檢測系統是否支持棧屬性。也可以給sysconf函數傳遞_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE來進行檢測。
當進程棧地址空間不夠用時,指定新建線程使用由malloc分配的空間作為自己的棧空間。通過pthread_attr_setstack和pthread_attr_getstack兩個函數分別設置和獲取線程的棧地址。
設置線程的棧地址:
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
成功:0;失敗:錯誤號
獲取線程的棧地址:
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
成功:0;失敗:錯誤號
參數:attr:指向一個線程屬性的指針
stackaddr:返回獲取的棧地址
stacksize:返回獲取的棧大小
線程的棧大小
當系統中有很多線程時,可能需要減小每個線程棧的默認大小,防止進程的地址空間不夠用。當線程調用的函數會分配很大的局部變量或者函數調用層次很深時,可能需要增大線程棧的默認大小。
函數pthread_attr_getstacksize和 pthread_attr_setstacksize可以設置或者獲取線程的棧大小。
設置線程棧大小:
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
成功:0;失敗:錯誤號
獲取線程棧大小:
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
成功:0;失敗:錯誤號
參數:attr:指向一個線程屬性的指針
stacksize:返回線程的堆棧大小
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 0x10000
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; //typedef size_t unsigned int
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++);
}
pthread_attr_destroy(&attr);
return 0;
}
更多精彩內容,請關注公眾號良許Linux,公眾內回復1024可免費獲得5T技術資料,包括:Linux,C/C++,Python,樹莓派,嵌入式,Java,人工智能,等等。公眾號內回復進群,邀請您進高手如雲技術交流群。
公眾號:良許Linux