對比
程序 線程
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 下線程的屬性是可以根據實際項目需要,進行設定,之前我們讨論的線程都是采用 線程的預設屬性,預設屬性已經可以解決絕大多數開發時遇到的問題。
如我們對程式的性能提出更高的要求那麼需 要設定線程屬性,比如可以通過設定線程棧的大小來降低記憶體的使用,增加最大線程個數。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB9UenpWT0kkaixGaykVMshFZwhnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4cjN2IjNzcTMzATOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
主要結構體成員:
- 線程分離狀态
- 線程棧大小(預設8M平均配置設定)
- 線程棧警戒緩沖區大小(位于棧末尾) 為了防止線程溢出
線程屬性說明
- 屬性值不能直接設定,須使用相關函數進行操作,初始化的函數為
,這個函數必須在pthread_attr_init
函數之前調用。之後須用pthread_create
函數來釋放資源。pthread_attr_destroy
- 線程屬性主要包括如下屬性:**作用域(scope)、棧尺寸(stacksize)、棧位址(stackaddress)、優先級(priority)、 分離的狀态(detachedstate)、排程政策和參數(schedulingpolicyandparameters)。**預設的屬性為非綁定、非分離、 預設的堆棧、與父程序同樣級别的優先級。
線程屬性控制流程
線程屬性初始化
注意:應先初始化線程屬性,再 pthread_create 建立線程
- 初始化線程屬性
成功:0;失敗:錯誤号int pthread_attr_init(pthread_attr_t *attr);
- 銷毀線程屬性所占用的資源
成功:0;失敗:錯誤号int pthread_attr_destroy(pthread_attr_t *attr);
線程的分離狀态
線程的分離狀态決定一個線程以什麼樣的方式來終止自己。
- 非分離狀态:線程的預設屬性是非分離狀态,這種情況下,原有的線程等待建立的線程結束。隻有當 pthread_join() 函數傳回時,建立的線程才算終止,才能釋放自己占用的系統資源。
- 分離狀态:分離線程沒有被其他的線程所等待,自己運作結束了,線程也就終止了,馬上釋放系統資源。應該 根據自己的需要,選擇适當的分離狀态。
線程分離狀态的函數:
設定線程屬性,分離 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);
}
示例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;
}
注意事項
- 如果設定一個線程為分離線程,而這個線程運作又非常快,它很可能在 pthread_create 函數傳回之前就終止了,它終止以後就可能将線程号和系統資源移交給其他的線程使用,這樣調用 pthread_create 的線程就得到了錯誤的線程号。
- 要避免這種情況可以采取一定的同步措施,最簡單的方法之一是可以在被建立的線 程裡調用 pthread_cond_timedwait 函數,讓這個線程等待一會兒,留出足夠的時間讓函數 pthread_create 傳回。
-
設 置一段等待時間,是在多線程程式設計裡常用的方法。但是注意不要使用諸如 wait()之類的函數,它們是使整個程序睡
眠,并不能解決線程同步的問題。
線程的棧大小
當系統中有很多線程時,可能需要減小每個線程棧的預設大小,防止程序的位址空間不夠用,當線程調用的函 數會配置設定很大的局部變量或者函數調用層次很深時,可能需要增大線程棧的預設大小。
函數
pthread_attr_getstacksize
和
pthread_attr_setstacksize
提供設定。
-
成功:0;失敗:錯誤号int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
-
成功:0;失敗:錯誤号int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);
- 參數:
attr:指向一個線程屬性的指針 stacksize:傳回線程的堆棧大小
預設線程棧均分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;
}
我的是能建立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;
}
NPTL(線程庫)
- 檢視目前 pthread 庫版本 getconfGNU_LIBPTHREAD_VERSION
- NPTL 實作機制(POSIX),NativePOSIXThreadLibrary
- 使用線程庫時 gcc 指定 –lpthread
線程使用注意事項
- 主線程退出其他線程不退出,主線程應調用 pthread_exit
-
避免僵屍線程
pthread_join
pthread_detach
pthread_create 指定分離屬性
被 join 線程可能在 join 函數傳回前就釋放完自己的所有記憶體資源,是以不應當傳回被回收線程棧中的值;
- malloc 和 mmap 申請的記憶體可以被其他線程釋放
- 應避免在多線程模型中調用 fork 除非,馬上 exec,子程序中隻有調用 fork 的線程存在,其他線程在子程序 中均 pthread_exit
- 信号的複雜語義很難和多線程共存,應避免在多線程引入信号機制