多線程編程(pthread)

簡介

pthread - POSIX threads 踪央。
POSIX 是IEEE為在各種UNIX操作系統(tǒng)上運行軟件阎毅,而定義API的一系列互相關聯(lián)的標準的總稱来涨,其正式稱呼為IEEE Std 1003晒夹,而國際標準名稱為 ISO/IEC 9945 佳镜。
因此僚稿,對于 pthread, 有其標準蟀伸,也有其實現(xiàn)蚀同,實現(xiàn)相對于標準會存在一定的出入,這也體現(xiàn)在后文的介紹中啊掏。

POSIX.1 中的定義

POSIX.1 為 POSIX threads 或 Pthreads 的線程編程指定了一組接口(函數(shù)蠢络、頭文件)。 一個進程可以包含多個線程脖律,所有線程都在執(zhí)行同一個程序谢肾。 這些線程共享相同的全局內存(數(shù)據(jù)段和堆段),但每個線程都有自己的堆棧(自動變量)小泉。

POSIX.1 要求線程共享一系列其他屬性:

  • parent process ID -
  • process group ID and session ID
  • controlling terminal
  • user and group IDs
  • open file descriptors
  • record locks
  • signal dispositions
  • file mode creation mask
  • current directory and root directory
  • interval timers and POSIX timers
  • nice value
  • resource limits
  • measurements of the consumption of CPU time and resources

除了 stack 之外芦疏,POSIX.1 還指定了以下屬性對于每個線程都是不同的、獨立的:

  • thread ID ( 數(shù)據(jù)類型是:pthread_t)
  • signal mask
  • the errno variable
  • alternate signal stack
  • real-time scheduling policy and priority

下列的Linux特性也是每個線程獨有的:

  • capabilities
  • CPU affinity

POSIX Thread 的 Linux 實現(xiàn)

隨著時間的推移微姊,Linux 上的 GNU C 庫提供了兩個線程實現(xiàn) :

  • LinuxThreads
  • NPTL (Native POSIX Threads Library)

LinuxThreads

這是原始的 Pthreads 實現(xiàn)酸茴。 從 glibc2.4 開始,不再支持此實現(xiàn)兢交。
LinuxThreads 實現(xiàn)在許多方面偏離了 POSIX.1 規(guī)范薪捍,因此我們暫不討論 LinuxThreads,除非我們使用 的 glibc 是低于 2.4 的版本配喳。

NPTL

這是現(xiàn)代 Pthreads 實現(xiàn)酪穿。 與 LinuxThreads 相比,NPTL 更符合 POSIX.1 規(guī)范的要求晴裹,并且在創(chuàng)建大量線程時具有更好的性能被济。 NPTL 從 glibc 2.3.2 開始可用,并且需要 Linux 2.6 內核中存在的功能涧团。

在 NPTL 中:

  • 一個進程中的所有線程都放在同一個線程組中只磷。
  • 線程組的所有成員共享相同的 PID经磅。
  • 不使用 manager thread。

NPTL 仍至少有一項不符合 POSIX.1:

  • 線程不共享同一個 nice value

NPTL 一些不符合 POSIX.1 發(fā)生在較舊的內核:

  • times(2) 和 getrusage(2) 返回的信息是每個線程的而不是進程范圍的(在內核 2.6.9 中修復)钮追。
  • 線程不共享 resource limits(在內核 2.6.10 中修復)预厌。
  • 線程不共享 interval timers(在內核 2.6.12 中修復)。
  • 只允許主線程使用 setsid 啟動新 session (在內核 2.6.16 中修復)元媚。
  • 只允許主線程使用 setpgid 使進程成為進程組 leader (在內核 2.6.16 中修復)轧叽。
  • 線程具有不同的備用信號堆棧設置。 但是惠毁,新線程的備用信號堆棧設置是從創(chuàng)建它的線程復制的犹芹,因此線程最初共享備用信號堆棧(在內核 2.6.16 中修復)。

請注意以下有關 NPTL 實現(xiàn)的進一步要點:

  • 如果 stack size soft resource limit 設置為 unlimited 以外的值鞠绰,則該值定義新線程的默認堆棧大小腰埂。 為了有效,這個限制必須在程序執(zhí)行之前設置蜈膨。
    • 可以使用 ulimit -s (shell 內置命令)進行設置屿笼。
    • 也可以在程序代碼中使用 setrlimit 設置 RLIMIT_STACK 的 大小。

pthread 編程

頭文件
#include <pthread.h>

pthread_t

typedef unsigned long int pthread_t;
  • 用于聲明線程ID翁巍、標識線程

pthread_create

int pthread_create(pthread_t *thread,
                          const pthread_attr_t *attr,
                          void *(*start_routine)(void *),
                          void *arg)

參數(shù):

  • pthread_t :線程 ID
  • pthread_attr_t :線程屬性
  • start_routine :函數(shù)指針驴一,該函數(shù)的返回值類型為 void,該函數(shù)的參數(shù)類型為 void
  • arg :將作為 start_routine 的參數(shù)

返回值:

  • EAGAIN : 資源不足灶壶,遇到系統(tǒng)對資源施加的限制肝断,無法創(chuàng)建另一個線程。 有許多限制可能會觸發(fā)此錯誤:1. 已達到 RLIMIT_NPROC 軟資源限制(通過 setrlimit 設置驰凛,通過 getrlimit 進行查詢)胸懈,該限制限制了真實用戶 ID 的進程和線程數(shù); 2. 已達到內核對進程和線程數(shù)的系統(tǒng)范圍限制恰响,/proc/sys/kernel/threads-max趣钱; 3. 達到了 PID 的最大量,/proc/sys/kernel/pid_max胚宦;4. 其他資源:如 打開的最大文件描述符數(shù)量首有,vm.max_map_count 等。
  • EINVAL : attr 中的設置無效枢劝。
  • EPERM : 沒有權限設置attr 中指定的調度策略和參數(shù)井联。

注:在創(chuàng)建大量線程時,通常易發(fā)生 EAGAIN 錯誤

pthread_attr_t

typedef struct
{
       int                               __detachstate;   // 線程的分離狀態(tài)
       int                               __schedpolicy;   // 線程調度策略
       struct sched_param                __schedparam;    // 線程的調度參數(shù)
       int                               __inheritsched;  // 線程的繼承性
       int                               __scope;         // 線程的作用域
       size_t                            __guardsize;     // 線程棧末尾的警戒緩沖區(qū)大小
       int                               __stackaddr_set; // 線程的棧設置
       void *                            __stackaddr;     // 線程堆棧的位置
       size_t                            __stacksize;     // 線程棧的大小
}pthread_attr_t;

修改 pthread_attr_t 的接口:
pthread_attr_init
pthread_attr_destroy

pthread_attr_setaffinity_np(3)
pthread_attr_setdetachstate(3)
pthread_attr_setschedpolicy(3)
pthread_attr_setinheritsched(3)
pthread_attr_setschedparam(3)
pthread_attr_setscope(3)
pthread_attr_setguardsize(3)
pthread_attr_setstack(3)
pthread_attr_setstackaddr(3)
pthread_attr_setstacksize(3)

pthread_cancel
向線程發(fā)送取消請求

int pthread_cancel(pthread_t thread);

向線程發(fā)送取消請求您旁。
目標線程是否以及何時對取消請求做出反應取決于該線程控制下的兩個屬性:可取消性狀態(tài)(enabled (the default for new threads) or disabled)和可取消性類型(asynchronous or deferred (the default for new threads))烙常。

  • 可取消性狀態(tài):如果線程 disable 取消,則取消請求將保持排隊狀態(tài)被冒,直到該線程 enable取消军掂。如果線程 enable 取消,則根據(jù)其可取消性類型確定何時發(fā)生取消昨悼。
  • 可取消性類型:asynchronous 意味著線程可以在任何時刻被取消蝗锥。deferred 意味著取消將被延遲,直到線程下一次調用作為取消點的函數(shù)率触。

當一個取消請求被發(fā)起時, 線程中將會進入以下步驟:

  1. 彈出并調用取消性清理處理程序终议。

  2. Thread-specific 的數(shù)據(jù)的析構函數(shù)將以 unspecified 的順序被調用。

  3. 線程終止葱蝗。

pthread_join

int pthread_join(pthread_t thread, void **retval);

pthread_join() 函數(shù)等待 thread 指定的線程終止穴张。 如果該線程已經終止,則 pthread_join() 立即返回两曼。 pthread_join() 指定的線程必須是 joinable 的皂甘。

如果 retval 不為 NULL,則 pthread_join() 將目標線程的退出狀態(tài)(即目標線程提供給 pthread_exit 的值)復制到 retval 指向的位置悼凑。 如果目標線程被取消偿枕,則 PTHREAD_CANCELED 被放置在 retval 指向的位置。

如果多個線程同時嘗試 join 同一個線程户辫,結果是不確定的渐夸。 如果調用 pthread_join() 的線程被取消,則目標線程將保持 joinable渔欢。

pthread_detach

int pthread_detach(pthread_t thread);

pthread_detach() 函數(shù)將線程標識的線程標記為已分離墓塌。 當一個分離的線程終止時,它的資源會自動釋放回系統(tǒng)奥额,而無需另一個線程 join 被終止的線程苫幢。

嘗試分離已分離的線程會導致未指定的行為。

返回值:

  • EINVAL :thread 不是 joinable thread披坏。
  • ESRCH :找不到線程ID所標識的線程态坦。

pthread_exit

void pthread_exit(void *retval);

終止調用線程。
pthread_exit() 函數(shù)終止調用線程并通過 retval 返回一個值(如果該線程可連接)棒拂,同一進程中的另一個線程可調用 pthread_join 獲取該返回值伞梯。

pthread_kill

 #include <signal.h>

int pthread_kill(pthread_t thread, int sig);

向線程發(fā)送信號。
pthread_kill() 函數(shù)將信號 sig 發(fā)送到線程帚屉,該線程與調用者處于同一進程中谜诫。 信號異步定向到線程。如果 sig 為 0攻旦,則不發(fā)送信號喻旷,但仍會執(zhí)行錯誤檢查。

pthread_yield

int pthread_yield(void);

主動讓出執(zhí)行權,主動讓出CPU時間田轧。

pthread_self

pthread_t pthread_self(void);

獲取調用者的線程ID俏扩。

pthread_setaffinity_np

 int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
                                  const cpu_set_t *cpuset);
 int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
                                  cpu_set_t *cpuset);

pthread_setaffinity_np 設置線程和CPU的親和性珠叔。
pthread_getaffinity_np 獲取線程和CPU的親和性录豺。

cpu_set_t 的常用接口:

#include <sched.h>
void CPU_ZERO(cpu_set_t *set);           // Clears set, so that it contains no CPUs.
void CPU_SET(int cpu, cpu_set_t *set);   // Add CPU cpu to set.
void CPU_CLR(int cpu, cpu_set_t *set);   // Remove CPU cpu from set.
int  CPU_ISSET(int cpu, cpu_set_t *set); // Test to see if CPU cpu is a member of set.

int cpu 表示第 cpu 號 cpu紧卒,可以從 /proc/cpuinfo 中查看 cpu 號(processor)质蕉,通常是從 0 ~ N
更多接口見:CPU_SET

pthread_setname_np

int pthread_setname_np(pthread_t thread, const char *name);
int pthread_getname_np(pthread_t thread, char *name, size_t len);

pthread_setname_np 函數(shù)可用于為線程設置唯一名稱蔓同,這對于調試多線程應用程序非常有用涮拗。
pthread_getname_np 函數(shù)可用于獲取線程的名稱乾戏。

pthread_mutex_t

互斥量:用于控制對資源的訪問

int pthread_mutex_init(pthread_mutex_t  *mutex,
           const pthread_mutexattr_t *attr);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 等價于 pthread_mutex_init(&mutex, NULL);

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

pthread_mutexattr_t

  • pthread_mutexattr_setpshared - 設置互斥鎖范圍設置互斥鎖的強健屬性
  • pthread_mutexattr_getpshared - 獲取互斥鎖范圍
  • pthread_mutexattr_settype - 設置互斥鎖的類型屬性
  • pthread_mutexattr_gettype - 獲取互斥鎖的類型屬性
  • pthread_mutexattr_setprotocol - 設置互斥鎖屬性的協(xié)議
  • pthread_mutexattr_getprotocol - 獲取互斥鎖屬性的協(xié)議
  • pthread_mutexattr_setprioceiling - 設置互斥鎖屬性的優(yōu)先級上限
  • pthread_mutexattr_getprioceiling - 獲取互斥鎖屬性的優(yōu)先級上限
  • pthread_mutex_setprioceiling - 設置互斥鎖的優(yōu)先級上限
  • pthread_mutex_getprioceiling - 獲取互斥鎖的優(yōu)先級上限
  • pthread_mutexattr_setrobust_np - 設置互斥鎖的強健屬性
  • pthread_mutexattr_getrobust_np - 獲取互斥鎖的強健屬性

pthread_mutexattr_settype

  • PTHREAD_MUTEX_TIMED_NP:當一個線程加鎖后,其余請求鎖的線程形成等待隊列三热,在解鎖后按優(yōu)先級獲得鎖鼓择。 (默認值
  • PTHREAD_MUTEX_ADAPTIVE_NP:動作最簡單的鎖類型,解鎖后所有線程重新競爭就漾。
  • PTHREAD_MUTEX_RECURSIVE_NP:允許同一線程對同一鎖成功獲得多次(遞歸鎖)呐能。當然也要解鎖多次。其余線程在解鎖時重新競爭抑堡。
  • PTHREAD_MUTEX_ERRORCHECK_NP:若同一線程請求同一鎖催跪,返回EDEADLK(進行死鎖檢測,返回死鎖的錯誤夷野,避免死鎖)懊蒸,否則與PTHREAD_MUTEX_TIMED_NP動作相同(直接死鎖)。
pthread_cond_t

條件變量:用于線程間同步

int pthread_cond_init(pthread_cond_t *cond,
           const pthread_condattr_t *attr);

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 等價于 pthread_cond_init(&cond, NULL);

int pthread_cond_wait(pthread_cond_t *cond,
           pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,
           pthread_mutex_t *mutex,
           const struct timespec *abstime);

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_destroy(pthread_cond_t *cond);

pthread_condattr_t

  • pthread_condattr_setshared - 設置條件變量的進程共享屬性
  • pthread_condattr_getshared - 獲取條件變量的進程共享屬性
  • pthread_condattr_setclock - 此函數(shù)用于設置pthread_cond_timewait函數(shù)使用的時鐘ID
  • pthread_condattr_getclock - 此函數(shù)獲取可被用于pthread_cond_timedwait函數(shù)的時鐘ID悯搔。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末骑丸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子妒貌,更是在濱河造成了極大的恐慌通危,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灌曙,死亡現(xiàn)場離奇詭異菊碟,居然都是意外死亡,警方通過查閱死者的電腦和手機在刺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門逆害,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚣驼,你說我怎么就攤上這事魄幕。” “怎么了颖杏?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵纯陨,是天一觀的道長。 經常有香客問我,道長翼抠,這世上最難降的妖魔是什么咙轩? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮阴颖,結果婚禮上臭墨,老公的妹妹穿的比我還像新娘。我一直安慰自己膘盖,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布尤误。 她就那樣靜靜地躺著侠畔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪损晤。 梳的紋絲不亂的頭發(fā)上软棺,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音尤勋,去河邊找鬼喘落。 笑死,一個胖子當著我的面吹牛最冰,可吹牛的內容都是我干的瘦棋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暖哨,長吁一口氣:“原來是場噩夢啊……” “哼赌朋!你這毒婦竟也來了?” 一聲冷哼從身側響起篇裁,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沛慢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后达布,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體团甲,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年黍聂,在試婚紗的時候發(fā)現(xiàn)自己被綠了躺苦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡产还,死狀恐怖圾另,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情雕沉,我是刑警寧澤集乔,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響扰路,放射性物質發(fā)生泄漏尤溜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一汗唱、第九天 我趴在偏房一處隱蔽的房頂上張望宫莱。 院中可真熱鬧,春花似錦哩罪、人聲如沸授霸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碘耳。三九已至,卻和暖如春框弛,著一層夾襖步出監(jiān)牢的瞬間辛辨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工瑟枫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斗搞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓慷妙,卻偏偏與公主長得像僻焚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膝擂,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內容