線程屬性設(shè)置
?專欄內(nèi)容:
參天引擎內(nèi)核架構(gòu)
本專欄一起來聊聊參天引擎內(nèi)核架構(gòu),以及如何實現(xiàn)多機的數(shù)據(jù)庫節(jié)點的多讀多寫,與傳統(tǒng)主備,MPP的區(qū)別,技術(shù)難點的分析,數(shù)據(jù)元數(shù)據(jù)同步,多主節(jié)點的情況下對故障容災(zāi)的支持。手寫數(shù)據(jù)庫toadb
本專欄主要介紹如何從零開發(fā),開發(fā)的步驟,以及開發(fā)過程中的涉及的原理,遇到的問題等,讓大家能跟上并且可以一起開發(fā),讓每個需要的人成為參與者。
本專欄會定期更新,對應(yīng)的代碼也會定期更新,每個階段的代碼會打上tag,方便階段學習。
?開源貢獻:
- toadb開源庫
個人主頁:我的主頁
管理社區(qū):開源數(shù)據(jù)庫
座右銘:天行健,君子以自強不息;地勢坤,君子以厚德載物.
前言
現(xiàn)代的CPU都是多core處理器,而且在intel處理器中每個core又可以多個processor,形成了多任務(wù)并行處理的硬件架構(gòu),在服務(wù)器端的處理器上架構(gòu)又有一些不同,傳統(tǒng)的采用SMP,也就是對稱的多任務(wù)處理架構(gòu),每個任務(wù)都可以對等的訪問所有內(nèi)存,外設(shè)等,而如今在ARM系列CPU上,多采用NUMA架構(gòu),它將CPU核分了幾個組,給每個組的CPU core分配了對應(yīng)的內(nèi)存和外設(shè),CPU訪問對應(yīng)的內(nèi)存和外設(shè)時速度最優(yōu),跨組訪問時性能會降底一些。
隨著硬件技術(shù)的持續(xù)發(fā)展,它們對一般應(yīng)用的性能優(yōu)化能力越來越強,同時對于服務(wù)器軟件的開發(fā),提出更高要求,要想達到極高的并發(fā)和性能,就需要充分利用當前硬件架構(gòu)的特點,對它們進行壓榨。那么,我們的應(yīng)用至少也是要采用多任務(wù)架構(gòu),不管是多線程還是多進程的多任務(wù)架構(gòu),才可以充分利用硬件的資源,達到高效的處理能力。
當然多任務(wù)框架的采用,不僅僅是多線程的執(zhí)行,需要對多任務(wù)下帶來的問題進行處理,如任務(wù)執(zhí)行返回值獲取,任務(wù)間數(shù)據(jù)的傳遞,任務(wù)執(zhí)行次序的協(xié)調(diào);當然也不是任務(wù)越多處理越快,要避免線程過多導(dǎo)致操作系統(tǒng)夯住,也要防止任務(wù)空轉(zhuǎn)過快導(dǎo)致CPU使用率飆高。
本專欄主要介紹使用多線程與多進程模型,如何搭建多任務(wù)的應(yīng)用框架,同時對多任務(wù)下的數(shù)據(jù)通信,數(shù)據(jù)同步,任務(wù)控制,以及CPU core與任務(wù)綁定等相關(guān)知識的分享,讓大家在實際開發(fā)中輕松構(gòu)建自已的多任務(wù)程序。
概述
前一篇博客介紹了創(chuàng)建線程的步驟和調(diào)用的接口,但是傳遞的參數(shù)都采用了默認值,其實線程有很多屬性值可以進行設(shè)置,這樣讓多線程的應(yīng)用運行更加的協(xié)調(diào),充分利用硬件資源。
本文就來分享一下線程的屬性,以及設(shè)置方法和接口,最后會分享一段示例代碼看一下設(shè)置效果。
特點說明一下,這里分享的linux thread 庫,是Native Posix Thread Library(NPTL),也就是符合posix的接口;為什么要強調(diào)這個呢,因為linux 下的線程庫有好幾種,各家實現(xiàn)都有一些差異,也會存在一些問題,而posix的這一套NTPL已經(jīng)被大家廣泛接受而大量使用,所以我們也以這套庫為基礎(chǔ)來介紹;編譯時需要加-lptrhead或libpthread庫引用。
線程屬性
線程屬性有很多,這里分類列舉一下。
屬性名 | 接口 | 描述 |
---|---|---|
棧屬性 | pthread_attr_getstack, pthread_attr_setstack | 設(shè)置棧地址和棧大小 |
棧地址 | pthread_attr_getstackaddr, pthread_attr_setstackaddr | 設(shè)置棧地址 |
棧大小 | pthread_attr_getstacksize, pthread_attr_setstacksize | 設(shè)置棧大小 |
堆棧保護區(qū) | pthread_attr_getguardsize, pthread_attr_setguardsize | 設(shè)置堆棧保護區(qū)大小 |
分離狀態(tài) | pthread_attr_getdetachstate, pthread_attr_setdetachstate | 設(shè)置線程的可連接或分離 |
調(diào)度繼承屬性 | pthread_attr_getinheritsched, pthread_attr_setinheritsched | 是否繼承調(diào)度屬性的設(shè)置 |
調(diào)度優(yōu)先級屬性 | pthread_attr_getschedparam, pthread_attr_setschedparam | 調(diào)度優(yōu)先級參數(shù)的設(shè)置 |
調(diào)度策略屬性 | pthread_attr_getschedpolicy, pthread_attr_setschedpolicy | 調(diào)度策略屬性的設(shè)置 |
調(diào)度資源的范圍 | pthread_attr_getscope, pthread_attr_setscope | 設(shè)置調(diào)度資源的范圍 |
CPU 親和性 | pthread_attr_getaffinity_np,pthread_attr_setaffinity_np | 設(shè)置線程運行時綁定的CPU core |
信號掩碼 | pthread_attr_getsigmask_np, pthread_attr_setsigmask_np | 信號掩碼設(shè)置 |
默認屬性 | pthread_getattr_default_np, pthread_setattr_default_np | 設(shè)置為線程默認屬性 |
獲取屬性 | pthread_getattr_np | 獲取線程實際屬性 |
主要分為四大類:
- 堆棧相關(guān)屬性,設(shè)置棧大小和起始地址,還可以設(shè)置保護區(qū),給越界留有緩沖區(qū);
- 調(diào)度相關(guān)屬性,CPU的綁定,調(diào)度策略等;
- 分離狀態(tài),對線程退出時,資源的回收方式的設(shè)置;
- 信號掩碼設(shè)置,對線程級別的信號中斷響應(yīng)設(shè)置;
棧屬性
屬性名 | 接口 | 描述 |
---|---|---|
棧屬性 | pthread_attr_getstack, pthread_attr_setstack | 設(shè)置棧地址和棧大小 |
棧地址 | pthread_attr_getstackaddr, pthread_attr_setstackaddr | 設(shè)置棧地址 |
棧大小 | pthread_attr_getstacksize, pthread_attr_setstacksize | 設(shè)置棧大小 |
堆棧保護區(qū) | pthread_attr_getguardsize, pthread_attr_setguardsize | 設(shè)置堆棧保護區(qū)大小 |
主要有四組接口,其中棧屬性設(shè)置包括了對棧地址和棧大小的設(shè)置,所以這里只介紹下面三組接口。
棧地址
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);
int pthread_attr_getstackaddr(const pthread_attr_t *restrict attr,
void **restrict stackaddr);
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
設(shè)置線程棧的起始地址,我們知道棧的地址是從起始地址開始從大到小的增長,也就是向下連續(xù)的分配空間,如果該地址超出了分配的棧區(qū)域的最高地址,就會發(fā)生棧溢出。
不建議平常使用單獨設(shè)置棧地址的功能 pthread_attr_setstackaddr
,由于無法提供指定增長方向或棧范圍的方法; 而pthread_attr_setstack
中指定了起始地址和stacksize參數(shù)指定的棧的范圍,可以分配連續(xù)的向下的區(qū)域。
棧大小
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);
常用的是對棧大小的設(shè)置,根據(jù)程序本身的特點,如并發(fā)線程多少,遞歸調(diào)用深度,分配大的結(jié)構(gòu)體數(shù)據(jù)等情況,決定是否需要調(diào)整默認棧大小。
棧保護區(qū)
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);
出于以下兩個原因,為應(yīng)用程序提供了 guardsize 屬性:
-
溢出保護可能會導(dǎo)致系統(tǒng)資源浪費。如果應(yīng)用程序創(chuàng)建大量線程,并且已知這些線程永遠不會溢出其棧,則可以關(guān)閉溢出保護區(qū)。通過關(guān)閉溢出保護區(qū),可以節(jié)省系統(tǒng)資源。
-
線程在棧上分配大型數(shù)據(jù)結(jié)構(gòu)時,可能需要較大的溢出保護區(qū)來檢測棧溢出。
guardsize 參數(shù)提供了對棧指針溢出的保護。如果創(chuàng)建線程的棧時使用了保護功能,則實現(xiàn)會在棧的溢出端分配額外內(nèi)存。此額外內(nèi)存的作用與緩沖區(qū)一樣,可以防止棧指針的棧溢出。如果應(yīng)用程序溢出到此緩沖區(qū)中,這個錯誤可能會導(dǎo)致 SIGSEGV 信號被發(fā)送給該線程。
如果 guardsize 為零,則不會為線程提供溢出保護區(qū)。如果 guardsize 大于零,則會為每個使用 attr 創(chuàng)建的線程提供大小至少為 guardsize 字節(jié)的溢出保護區(qū)。缺省情況下,線程具有實現(xiàn)定義的非零溢出保護區(qū)。
允許合乎慣例的實現(xiàn),將 guardsize 的值向上舍入為可配置的系統(tǒng)變量 PAGESIZE 的倍數(shù)。
調(diào)度屬性
屬性名 | 接口 | 描述 |
---|---|---|
調(diào)度繼承屬性 | pthread_attr_getinheritsched, pthread_attr_setinheritsched | 是否繼承調(diào)度屬性的設(shè)置 |
調(diào)度優(yōu)先級屬性 | pthread_attr_getschedparam, pthread_attr_setschedparam | 調(diào)度優(yōu)先級參數(shù)的設(shè)置 |
調(diào)度策略屬性 | pthread_attr_getschedpolicy, pthread_attr_setschedpolicy | 調(diào)度策略屬性的設(shè)置 |
調(diào)度資源的范圍 | pthread_attr_getscope, pthread_attr_setscope | 設(shè)置調(diào)度資源的范圍 |
CPU 親和性 | pthread_attr_getaffinity_np,pthread_attr_setaffinity_np | 設(shè)置線程運行時綁定的CPU core |
線程調(diào)度屬性主要有以下幾種:
- 繼承屬性
- 調(diào)度參數(shù)屬性
- 調(diào)度策略屬性
- CPU親和性屬性
繼承屬性
int pthread_attr_setinheritsched(pthread_attr_t *attr,
int inheritsched);
int pthread_attr_getinheritsched(pthread_attr_t *attr,
int *inheritsched);
- inherit 值為
PTHREAD_INHERIT_SCHED
表示新建的線程將繼承創(chuàng)建者線程中定義的調(diào)度策略, 將忽略在 pthread_create() 調(diào)用中定義的所有調(diào)度屬性。 - 如果使用缺省值
PTHREAD_EXPLICIT_SCHED
,則將使用 pthread_create() 調(diào)用中的屬性。
調(diào)度策略屬性
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy);
這里的策略支持三種取值:
當policy 取值為以下:
- SCHED_FIFO, 先來先服務(wù);
- SCHED_RR, 時間片輪轉(zhuǎn);
- SCHED_OTHER, 普通策略;
前兩種是realtime,實時系統(tǒng)的調(diào)度策略,一般不會使用,它們兩個支持優(yōu)先級的設(shè)置,范圍是1-99;
第三種是用戶線程默認的策略類型,在內(nèi)核中的命名是SCHED_NORMAL
, 不支持優(yōu)先級設(shè)置,必須為0;
當然在SCHED_OTHER
策略下的各用戶線程之間可以通過調(diào)整nice值,進行優(yōu)先級調(diào)整,它的范圍為-20 - 19之間,越小優(yōu)先級越高。
優(yōu)先級屬性
int pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param);
int pthread_attr_getschedparam(pthread_attr_t *attr,
struct sched_param *param);
調(diào)度參數(shù)在結(jié)構(gòu)sched_param
中定義,僅支持優(yōu)先級參數(shù)設(shè)定。
優(yōu)先級參數(shù)僅在支持的調(diào)度策略下設(shè)置才有效,在SCHED_OTHER
, SCHED_IDLE
, SCHED_BATCH
這三種策略下,優(yōu)先級必須設(shè)置為0;
在SCHED_FIFO
, SCHED_RR
這兩種實時調(diào)度策略下,優(yōu)先級范圍為1-99,數(shù)字越大優(yōu)先級越高;
新創(chuàng)建的線程以此優(yōu)先級運行, 簡單示例代碼如下:
pthread_attr_t tattr;
int newprio;
sched_param param;
/* set the priority; others are unchanged */
param.sched_priority = 10;
/* set the new scheduling param */
ret = pthread_attr_setschedparam (&tattr, ¶m);
調(diào)度資源范圍屬性
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope);
contentionscope的取值如下:
- PTHREAD_SCOPE_SYSTEM, 線程在搶占資源,與它競爭的線程是系統(tǒng)中的所有線程;
- PTHREAD_SCOPE_PROCESS, 線程在搶占資源時,與它競爭的線程是本進程創(chuàng)建的線程,優(yōu)先級依賴與策略和優(yōu)先級設(shè)定;
CPU親和性屬性
int pthread_attr_setaffinity_np(pthread_attr_t *attr,
size_t cpusetsize, const cpu_set_t *cpuset);
int pthread_attr_getaffinity_np(pthread_attr_t *attr,
size_t cpusetsize, cpu_set_t *cpuset);
參數(shù)說明
- cpusetsize, 是第三個參數(shù)的size, 也就是sizeof(cpu_set_t);
- cpuset, 指定CPU core的掩碼,使用
CPU_ZERO(&set);
和CPU_SET(numCpu, &set);
兩個宏來設(shè)定,numCpu指定綁定的core或thread編號,是整型數(shù)字;
參看機制的CPU 數(shù)量和core數(shù)量
[senllang@hatch example_03]$ lscpu | egrep -i 'core.*:|socket'
Thread(s) per core: 2
Core(s) per socket: 8
Socket(s): 1
這里有一個CPU,包含8個core,每個core可以有兩個線程,那就是可以有16個掩碼值,設(shè)置時編號從0-15;
有時CPU會采用NUMA架構(gòu),那么相關(guān)線程需要設(shè)置到同一個Node的CPU編號下。
分離屬性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
detachstate的取值如下:
- PTHREAD_CREATE_DETACHED, 創(chuàng)建分離狀態(tài)的線程,此時不用關(guān)注線程退出時的資源回收;
- PTHREAD_CREATE_JOINABLE, 創(chuàng)建可連接狀態(tài)的線程,線程退出時,需要使用pthread_join對線程資源回收;默認參數(shù)就是可連接狀態(tài)的線程。
如果線程以PTHREAD_CREATE_JOINABLE創(chuàng)建后,沒有時機調(diào)用pthread_join時,還可以調(diào)用pthread_detach 函數(shù),將指定線程置為分離狀態(tài),這樣系統(tǒng)會自動回收線程資源。
如果線程以PTHREAD_CREATE_JOINABLE創(chuàng)建后,沒有調(diào)用pthread_join,會造成一定的內(nèi)存泄漏,這里一定要注意。
信號屬性
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <pthread.h>
int pthread_attr_setsigmask_np(pthread_attr_t *attr,
const sigset_t *sigmask);
int pthread_attr_getsigmask_np(const pthread_attr_t *attr,
sigset_t *sigmask);
設(shè)置線程級別的信號掩碼,也就是那些信號會被阻塞。
sigset_t 類型的操作,需要使用一組信號掩碼操作函數(shù)
- int sigemptyset (sigset_t *set) ,清空信號掩碼變量
- int sigfillset (sigset_t *set) , 填充所有信號掩碼
- int sigaddset (sigset_t *set, int signum) ,將某個信號添加到掩碼中
- int sigdelset (sigset_t *set, int signum) ,將某個信號從掩碼中移除
- int sigismember (const sigset_t *set, int signum), 檢測某個信號是否在掩碼中
默認屬性
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <pthread.h>
int pthread_getattr_default_np(pthread_attr_t *attr);
int pthread_setattr_default_np(pthread_attr_t *attr);
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr);
前兩個函數(shù)是將線程屬性設(shè)置為默認值,也就是創(chuàng)建線程時,將線程屬性設(shè)置為NULL,這兩者是等價的。
第三個函數(shù)是獲取指定線程的屬性,可以在線程運行過程中獲取線程屬性。
總結(jié)
本文主要分享了線程屬性相關(guān)的接口,以及部分屬性的含義,如何正確使用;在應(yīng)用編程時,大多數(shù)情況下都會采用多線程并發(fā)的架構(gòu),線程屬性的正確使用,能夠幫助我們有提高CPU的利用效率,同時在使用過程中避夠資源泄漏也非常關(guān)鍵。
在gitCode上分享了工程hatchCode,會不斷增加多線程并發(fā)的案例代碼,請大家關(guān)注保留。
結(jié)尾
非常感謝大家的支持,在瀏覽的同時別忘了留下您寶貴的評論,如果覺得值得鼓勵,請點贊,收藏,我會更加努力!文章來源:http://www.zghlxwxcb.cn/news/detail-772904.html
作者郵箱:study@senllang.onaliyun.com
如有錯誤或者疏漏歡迎指出,互相學習。文章來源地址http://www.zghlxwxcb.cn/news/detail-772904.html
到了這里,關(guān)于【linux 多線程并發(fā)】線程屬性設(shè)置與查看,綁定CPU,線程分離與可連接,避夠多線程下的內(nèi)存泄漏的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!