Linux系統程式設計(27)——執行緒控制
程式在各自獨立的地址空間中執行,程式之間共享資料需要用mmap或者程式間通訊機制,那麼如何在一個程式的地址空間中執行多個執行緒呢。有些情況需要在一個程式中同時執行多個控制流程,這時候執行緒就派上了用場,比如實現一個圖形介面的下載軟體,一方面需要和使用者互動,等待和處理使用者的滑鼠鍵盤事件,另一方面又需要同時下載多個檔案,等待和處理從多個網路主機發來的資料,這些任務都需要一個“等待-處理”的迴圈,可以用多執行緒實現,一個執行緒專門負責與使用者互動,另外幾個執行緒每個執行緒負責和一個網路主機通訊。
main函式和訊號處理函式是同一個程式地址空間中的多個控制流程,多執行緒也是如此,但是比訊號處理函式更加靈活,訊號處理函式的控制流程只是在訊號遞達時產生,在處理完訊號之後就結束,而多執行緒的控制流程可以長期並存,作業系統會在各執行緒之間排程和切換,就像在多個程式之間排程和切換一樣。由於同一程式的多個執行緒共享同一地址空間,因此Text Segment、Data Segment都是共享的,如果定義一個函式,在各執行緒中都可以呼叫,如果定義一個全域性變數,在各執行緒中都可以訪問到,除此之外,各執行緒還共享以下程式資源和環境:
檔案描述符表
每種訊號的處理方式(SIG_IGN、SIG_DFL或者自定義的訊號處理函式)
當前工作目錄
使用者id和組id
但有些資源是每個執行緒各有一份的:
執行緒id
上下文,包括各種暫存器的值、程式計數器和棧指標
棧空間
errno變數
訊號遮蔽字
排程優先順序
我們將要學習的執行緒庫函式是由POSIX標準定義的,稱為POSIX thread或者pthread。在Linux上執行緒函式位於libpthread共享庫中,因此在編譯時要加上-lpthread選項。
1. 建立執行緒
#include <pthread.h>
int pthread_create(pthread_t *restrictthread,
constpthread_attr_t *restrict attr,
void*(*start_routine)(void*), void *restrict arg);
返回值:成功返回0,失敗返回錯誤號。以前學過的系統函式都是成功返回0,失敗返回-1,而錯誤號儲存在全域性變數errno中,而pthread庫的函式都是通過返回值返回錯誤號,雖然每個執行緒也都有一個errno,但這是為了相容其它函式介面而提供的,pthread庫本身並不使用它,通過返回值返回錯誤碼更加清晰。
在一個執行緒中呼叫pthread_create()建立新的執行緒後,當前執行緒從pthread_create()返回繼續往下執行,而新的執行緒所執行的程式碼由我們傳給pthread_create的函式指標start_routine決定。start_routine函式接收一個引數,是通過pthread_create的arg引數傳遞給它的,該引數的型別為void *,這個指標按什麼型別解釋由呼叫者自己定義。start_routine的返回值型別也是void *,這個指標的含義同樣由呼叫者自己定義。start_routine返回時,這個執行緒就退出了,其它執行緒可以呼叫pthread_join得到start_routine的返回值,類似於父程式呼叫wait(2)得到子程式的退出狀態,稍後詳細介紹pthread_join。
pthread_create成功返回後,新建立的執行緒的id被填寫到thread引數所指向的記憶體單元。我們知道程式id的型別是pid_t,每個程式的id在整個系統中是唯一的,呼叫getpid(2)可以獲得當前程式的id,是一個正整數值。執行緒id的型別是thread_t,它只在當前程式中保證是唯一的,在不同的系統中thread_t這個型別有不同的實現,它可能是一個整數值,也可能是一個結構體,也可能是一個地址,所以不能簡單地當成整數用printf列印,呼叫pthread_self(3)可以獲得當前執行緒的id。
attr參數列示執行緒屬性,本章不深入討論執行緒屬性,所有程式碼例子都傳NULL給attr引數,表示執行緒屬性取預設值。首先看一個簡單的例子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_t ntid;
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid= getpid();
tid= pthread_self();
printf("%spid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
void *thr_fn(void *arg)
{
printids(arg);
returnNULL;
}
int main(void)
{
interr;
err= pthread_create(&ntid, NULL, thr_fn, "new thread: ");
if(err != 0) {
fprintf(stderr,"can't create thread: %s\n", strerror(err));
exit(1);
}
printids("mainthread:");
sleep(1);
return0;
}
編譯執行結果如下:
main thread: pid 7398 tid 3084450496(0xb7d8fac0)
new thread: pid 7398 tid 3084446608 (0xb7d8eb90)
可知在Linux上,thread_t型別是一個地址值,屬於同一程式的多個執行緒呼叫getpid(2)可以得到相同的程式號,而呼叫pthread_self(3)得到的執行緒號各不相同。
由於pthread_create的錯誤碼不儲存在errno中,因此不能直接用perror(3)列印錯誤資訊,可以先用strerror(3)把錯誤碼轉換成錯誤資訊再列印。
2. 終止執行緒
如果需要只終止某個執行緒而不終止整個程式,可以有三種方法:
從執行緒函式return。這種方法對主執行緒不適用,從main函式return相當於呼叫exit。
一個執行緒可以呼叫pthread_cancel終止同一程式中的另一個執行緒。
執行緒可以呼叫pthread_exit終止自己。
用pthread_cancel終止一個執行緒分同步和非同步兩種情況,比較複雜,本章不打算詳細介紹,讀者可以參考[APUE2e]。下面介紹pthread_exit的和pthread_join的用法。
#include <pthread.h>
void pthread_exit(void *value_ptr);
value_ptr是void *型別,和執行緒函式返回值的用法一樣,其它執行緒可以呼叫pthread_join獲得這個指標。
需要注意,pthread_exit或者return返回的指標所指向的記憶體單元必須是全域性的或者是用malloc分配的,不能線上程函式的棧上分配,因為當其它執行緒得到這個返回指標時執行緒函式已經退出了。
#include <pthread.h>
int pthread_join(pthread_t thread, void**value_ptr);
返回值:成功返回0,失敗返回錯誤號
呼叫該函式的執行緒將掛起等待,直到id為thread的執行緒終止。thread執行緒以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
如果thread執行緒通過return返回,value_ptr所指向的單元裡存放的是thread執行緒函式的返回值。
如果thread執行緒被別的執行緒呼叫pthread_cancel異常終止掉,value_ptr所指向的單元裡存放的是常數PTHREAD_CANCELED。
如果thread執行緒是自己呼叫pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的引數。
如果對thread執行緒的終止狀態不感興趣,可以傳NULL給value_ptr引數。
看下面的例子 :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thr_fn1(void *arg)
{
printf("thread1 returning\n");
return(void *)1;
}
void *thr_fn2(void *arg)
{
printf("thread2 exiting\n");
pthread_exit((void*)2);
}
void *thr_fn3(void *arg)
{
while(1){
printf("thread3 writing\n");
sleep(1);
}
}
int main(void)
{
pthread_t tid;
void *tret;
pthread_create(&tid,NULL, thr_fn1, NULL);
pthread_join(tid,&tret);
printf("thread1 exit code %d\n", (int)tret);
pthread_create(&tid,NULL, thr_fn2, NULL);
pthread_join(tid,&tret);
printf("thread2 exit code %d\n", (int)tret);
pthread_create(&tid,NULL, thr_fn3, NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid,&tret);
printf("thread3 exit code %d\n", (int)tret);
return0;
}
執行結果是:
thread 1 returning
thread 1 exit code 1
thread 2 exiting
thread 2 exit code 2
thread 3 writing
thread 3 writing
thread 3 writing
thread 3 exit code -1
可見在Linux的pthread庫中常數PTHREAD_CANCELED的值是-1。可以在標頭檔案pthread.h中找到它的定義:
#define PTHREAD_CANCELED ((void *) -1)一般情況下,執行緒終止後,其終止狀態一直保留到其它執行緒呼叫pthread_join獲取它的狀態為止。但是執行緒也可以被置為detach狀態,這樣的執行緒一旦終止就立刻回收它佔用的所有資源,而不保留終止狀態。不能對一個已經處於detach狀態的執行緒呼叫pthread_join,這樣的呼叫將返回EINVAL。對一個尚未detach的執行緒呼叫pthread_join或pthread_detach都可以把該執行緒置為detach狀態,也就是說,不能對同一執行緒呼叫兩次pthread_join,或者如果已經對一個執行緒呼叫了pthread_detach就不能再呼叫pthread_join了。
#include <pthread.h>
int pthread_detach(pthread_t tid);
返回值:成功返回0,失敗返回錯誤號。
相關文章
- 【linux】系統程式設計-5-執行緒Linux程式設計執行緒
- Linux系統程式設計(28)——執行緒間同步Linux程式設計執行緒
- Linux系統程式設計(29)——執行緒間同步(續篇)Linux程式設計執行緒
- Python系統程式設計之執行緒Python程式設計執行緒
- linux 多執行緒程式設計Linux執行緒程式設計
- Linux系統下的多執行緒程式設計入門(轉)Linux執行緒程式設計
- Linux 多執行緒程式設計(不限Linux)Linux執行緒程式設計
- PHP系統程式設計--01.多程式與多執行緒PHP程式設計執行緒
- Linux下的多執行緒程式設計Linux執行緒程式設計
- Linux C++ 多執行緒程式設計LinuxC++執行緒程式設計
- 【作業系統】程式的描述與控制[執行緒](4)作業系統執行緒
- 程式設計思想之多執行緒與多程式(1):以作業系統的角度述說執行緒與程式程式設計執行緒作業系統
- 多執行緒程式設計執行緒程式設計
- 執行緒程式設計(一)執行緒程式設計
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- Linux下的多執行緒程式設計(轉)Linux執行緒程式設計
- .NET多執行緒程式設計(4):執行緒池和非同步程式設計 (轉)執行緒程式設計非同步
- 使用執行緒池優化多執行緒程式設計執行緒優化程式設計
- iOS多執行緒程式設計:執行緒同步總結iOS執行緒程式設計
- .NET多執行緒程式設計(3):執行緒同步 (轉)執行緒程式設計
- java執行緒程式設計(一):執行緒基礎(轉)Java執行緒程式設計
- POSIX執行緒程式設計起步(2)-執行緒同步 (轉)執行緒程式設計
- Posix執行緒程式設計指南(3)-執行緒同步 (轉)執行緒程式設計
- JavaScript多執行緒程式設計JavaScript執行緒程式設計
- Boost多執行緒程式設計執行緒程式設計
- UNIX多執行緒程式設計執行緒程式設計
- 多執行緒程式設計(轉)執行緒程式設計
- Linux學習--執行緒控制Linux執行緒
- Linux多執行緒伺服器端程式設計Linux執行緒伺服器程式設計
- Linux多執行緒程式設計———重點區分Linux執行緒程式設計
- 20170526-27關於GCD控制執行緒併發數,多執行緒併發數控制GC執行緒
- 多執行緒程式設計基礎(一)-- 執行緒的使用執行緒程式設計
- Posix執行緒程式設計指南(4)-執行緒終止 (轉)執行緒程式設計
- Linux 環境多執行緒程式設計基礎設施Linux執行緒程式設計
- Linux環境多執行緒程式設計基礎設施Linux執行緒程式設計
- 【作業系統】程式與執行緒作業系統執行緒
- 多執行緒-執行緒控制之休眠執行緒執行緒