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程式設計執行緒
- Python系統程式設計之執行緒Python程式設計執行緒
- Linux C++ 多執行緒程式設計LinuxC++執行緒程式設計
- 【作業系統】程式的描述與控制[執行緒](4)作業系統執行緒
- Linux多執行緒伺服器端程式設計Linux執行緒伺服器程式設計
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- python 多執行緒程式設計Python執行緒程式設計
- JavaScript多執行緒程式設計JavaScript執行緒程式設計
- Python多執行緒程式設計Python執行緒程式設計
- 多執行緒程式設計基礎(一)-- 執行緒的使用執行緒程式設計
- Linux C/C++程式設計中的多執行緒程式設計基本概念LinuxC++程式設計執行緒
- LINUX作業系統知識:程式與執行緒詳解Linux作業系統執行緒
- 【Linux網路程式設計-5】多執行緒服務端Linux程式設計執行緒服務端
- 【作業系統】程式與執行緒作業系統執行緒
- windows核心程式設計--執行緒池Windows程式設計執行緒
- 多執行緒程式設計ExecutorService用法執行緒程式設計
- 29. 多執行緒程式設計執行緒程式設計
- 併發程式設計之:執行緒程式設計執行緒
- 執行緒控制之休眠執行緒執行緒
- Java併發程式設計之執行緒安全、執行緒通訊Java程式設計執行緒
- C#多執行緒程式設計實戰1.1建立執行緒C#執行緒程式設計
- 多執行緒程式設計基礎(二)-- 執行緒池的使用執行緒程式設計
- C#多執行緒程式設計-基元執行緒同步構造C#執行緒程式設計
- linux系統時間程式設計(9) 計算程式片段執行時間clock函式Linux程式設計函式
- 純C語言設計的執行緒,讓微控制器不用作業系統的情況下,支援執行緒和事件C語言執行緒作業系統事件
- Java基礎之多執行緒程式設計Java執行緒程式設計
- 從執行緒到併發程式設計執行緒程式設計
- Windows程式設計系列:遠執行緒注入Windows程式設計執行緒
- 深入理解多執行緒程式設計執行緒程式設計
- 併發程式設計與執行緒安全程式設計執行緒
- [02] 多執行緒邏輯程式設計執行緒程式設計
- Java併發程式設計:Java執行緒Java程式設計執行緒
- java併發程式設計——執行緒池Java程式設計執行緒
- java併發程式設計——執行緒同步Java程式設計執行緒
- iOS開發-多執行緒程式設計iOS執行緒程式設計
- JavaSE高階程式設計之多執行緒Java程式設計執行緒
- Task+ConcurrentQueue多執行緒程式設計執行緒程式設計
- Java併發程式設計之執行緒篇之執行緒中斷(三)Java程式設計執行緒