线程的属性 —— 分离的状态(detached state)、栈地址(stack address)、栈大小(stack size)
參考:(四十二)線程——線程屬性
作者:FadeFarAway
發布時間:2017-01-17 14:09:55
網址:https://blog.csdn.net/FadeFarAway/article/details/54576771
目錄
- 引入
- 線程屬性初始化
- 一、線程的分離狀態(detached state)
- Demo
- 二、線程的棧地址(stack address)
- 三、線程的棧大小(stack size)
- Demo
- 細節注意
引入
linux下線程的屬性是可以根據實際項目需要,進行設置。之前我們討論的線程都是采用線程的默認屬性,默認屬性已經可以解決絕大多數開發時遇到的問題。如我們對程序的性能提出更高的要求那么需要設置線程屬性,比如可以通過設置線程棧的大小來降低內存的使用,增加最大線程個數。
typedef struct {int etachstate; //線程的分離狀態int schedpolicy; //線程調度策略(線程優先級)structsched_param schedparam; //線程的調度參數int inheritsched; //線程的繼承性int scope; //線程的作用域size_t guardsize; //線程棧末尾的警戒緩沖區大小(棧溢出時可以多溢出的大小)int stackaddr_set; //線程的棧設置void* stackaddr; //線程棧的位置size_t stacksize; //線程棧的大小 }pthread_attr_t;注:目前線程屬性在內核中不是直接這么定義的,抽象太深不宜理解,為了方便,使用早期的線程屬性定義,兩者之間定義的主要元素差別不大。屬性值不能直接設置,必須使用相關函數進行操作,初始化的函數為pthread_attr_init,這個函數必須在pthread_create函數之前調用。之后須用pthread_attr_destroy函數來釋放資源。
線程屬性主要包括:分離的狀態(detached state)、棧地址(stack address)、棧尺寸(stack size)、優先級(priority)、作用域(scope)、調度策略和參數(scheduling policy and parameters)。
默認的屬性為:非綁定、非分離、缺省M的堆棧、與父進程同樣級別的優先級。
線程屬性初始化
先初始化線程屬性,再使用pthread_create創建線程。
#include <pthread.h>int pthread_attr_init(pthread_attr_t *attr); //初始化線程屬性 int pthread_attr_destroy(pthread_attr_t *attr); //銷毀線程屬性所占用的資源一、線程的分離狀態(detached state)
線程的分離狀態決定一個線程以什么樣的方式來終止自己:
-
非分離狀態:線程的默認屬性是非分離狀態,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程才算終止,才能釋放自己占用的系統資源。
-
分離狀態:分離線程沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。
應該根據自己的需要,選擇適當的分離狀態。
線程分離狀態的函數:
#include <pthread.h>int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // 設置線程屬性,分離or非分離 int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); // 獲取線程屬性,分離or非分離pthread_attr_t *attr:被已初始化的線程屬性 int *detachstate:可選為PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)【要注意的一點】如果設置一個線程為分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以后就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被創建的線程里調用pthread_cond_timedwait函數,讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,并不能解決線程同步的問題。
Demo
/**#include <pthread.h>*int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);* //設置線程屬性,分離or非分 離*int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);* //獲取程屬性,分離or非分離* *pthread_attr_t *attr:被已初始化的線程屬性*int *detachstate:可選為PTHREAD_CREATE_DETACHED(分離線程)* 和PTHREAD _CREATE_JOINABLE(非分離線程)*/ #include <stdio.h> #include <pthread.h> #include <string.h> #include <stdlib.h>void *th_fun(void *arg) {int n = 15;while(n--){printf("%x %d\n",(int)pthread_self(), n);sleep(1);}return (void *)1;//由于被設置為分離態所以這個返回值不能被獲取 }int main(void) {pthread_t tid;pthread_attr_t attr; //保存線程的屬性,現在里面是垃圾值int err;pthread_attr_init(&attr);// 初始化線程屬性結構體,初始化之后就保存著線程屬性的默認值//參考上面,先調用初始化函數(pthread_attr_init)之后才能設置線程屬性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //設置為分離線程// 創建線程,注意第二個參數pthread_create(&tid, &attr, th_fun, NULL);//因為分離了所以會出現Invalid argument非法的參數err = pthread_join(tid, NULL);while(1){if(err != 0){printf("%s\n",strerror(err));sleep(10);pthread_exit((void *)1);}}pthread_attr_destroy(&attr);//銷毀線程屬性所占用的資源return 0; }運行結果:
二、線程的棧地址(stack address)
POSIX.1定義了兩個常量_POSIX_THREAD_ATTR_STACKADDR 和_POSIX_THREAD_ATTR_STACKSIZE 檢測系統是否支持棧屬性。也可以給sysconf函數傳遞_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE來進行檢測。
棧有一個默認大小(點擊查看博文),當進程棧地址空間不夠用時,指定新建線程使用由malloc分配的空間作為自己的棧空間。通過pthread_attr_setstackaddr和pthread_attr_getstackaddr兩個函數分別設置和獲取線程的棧地址。傳給pthread_attr_setstackaddr函數的地址是緩沖區的低地址(不一定是棧的開始地址,棧可能從高地址往低地址增長)。
#include <pthread.h>int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);attr: 指向一個線程屬性的指針 stackaddr: 返回獲取的棧地址 返回值:若是成功返回0,否則返回錯誤的編號說 明:pthread_attr_getstackaddr函數已過時,一般用下面講到的pthread_attr_getstack來代替三、線程的棧大小(stack size)
當系統中有很多線程時,可能需要減小每個線程棧的默認大小,防止進程的地址空間不夠用;當線程調用的函數會分配很大的局部變量或者函數調用層次很深時,可能需要增大線程棧的默認大小。
函數pthread_attr_getstacksize和 pthread_attr_setstacksize提供獲取和設置。
除上述對棧設置的函數外,還有以下兩個函數可以獲取和設置線程棧屬性(上面的一組在現在的開發過程中往往不會使用)
#include <pthread.h>int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);attr 指向一個線程屬性的指針 stackaddr 返回獲取的棧地址 stacksize 返回獲取的棧大小 返回值:若是成功返回0,否則返回錯誤的編號Demo
/** 獲取和設置線程的棧大小* #include <pthread.h>* int pthread_attr_setstack(pthread_attr_t *attr,* void *stackaddr,* size_t stacksize);* int pthread_attr_getstack(pthread_attr_t *attr,* void **stackaddr,* size_t *stacksize);* attr 指向一個線程屬性的指針* stackaddr 返回獲取的棧地址* stacksize 返回獲取的棧大小* 返回值:若是成功返回0,否則返回錯誤的編號*/ #include <stdio.h> #include <pthread.h> #include <string.h> #include <stdlib.h>#define SIZE 0x10000int print_ntimes(char *str) {sleep(1);printf("%s\n", str);return 0; }void *th_fun(void *arg) {int n = 3;while (n--)print_ntimes("hello xwp\n");//在線程中也可以調用別的函數 }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);//獲取棧信息printf("stackadd=%p\n", stackaddr); //打印棧的地址printf("stacksize=%x\n", (int)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");elseprintf("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; }運行結果:
循環創建線程,每個線程分配占用堆空間大小0x10000,最終創建2145次線程時提示空間不足,也就是堆空間被占用盡(我自己理解的)。
細節注意
1. 主線程退出其他線程不退出,主線程應調用ptrhed_exit
2. 避免僵線程(可以使用一下的方法)
1、join
2、pthread_deatch
3、pthread_create指定分離屬性
注:被join線程可能在join函數返回前就釋放完自己的所有內存資源,所以不應當返回被回收線程棧中的值;
3. malloc和mmap申請的內存可以被其他線程釋放
4. 如果線程終止時沒有釋放加鎖的互斥量,則該互斥量不能再被使用
5. 應避免在多線程模型中調用fork除非,馬上exec,子進程中只有調用fork的線程存在,其他線程在子進程中均pthread_exit
6. 信號的復雜語義很難和多線程共存,應避免在多線程引入信號機制
總結
以上是生活随笔為你收集整理的线程的属性 —— 分离的状态(detached state)、栈地址(stack address)、栈大小(stack size)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php调用pdf虚拟打印机,电脑中怎么安
- 下一篇: android-波浪效果ripple-b