有的時候,我們需要在一個基礎中同時運行多個控制流程程。例如:一個圖形介面的下載軟體,在處理下載任務的同時,還必須響應介面的對任務的停止,刪除等控制操作。這個時候就需要用到線程來實現並行作業。
和訊號處理函數的控制在處理完訊號之後就結束不同的時,而多線程的控制流程程可以長期並存,作業系統會在各線程之間調度和切換,就像在多個進程之間調度和切換一樣,但建立線程開銷要比進程小得多。因此,線程往往也被稱作輕量級的進程。
由於同一進程的多個線程共用同一地址空間,資料區段是共用的,如果定義一個全域變數,在各線程中都可以訪問到。但有,些資源依然是每個線程各有一份的:
- 線程id
- 上下文,包括各種寄存器的值、程式計數器和棧指標
- 棧空間
- errno變數
- 訊號屏蔽字
- 調度優先順序
我們一般用到的是由POSIX標準定義的線程庫函數,稱為POSIX thread或者pthread。在Linux上線程函數位於libpthread共用庫中,因此在編譯時間要加上-lpthread選項。
建立線程
在POSIX庫中,建立線程是通過函數實現的,它的聲明如下:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
它有四個參數,
- 第一個參數thread為建立後的線程標識符的指標。
- 第二個參數attr為線程屬性,可選為PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程),一般是直接用NULL。
- 第三個參數start_routine為線程的運行函數
- 第四個參數arg為線程運行函數的參數
- 建立成功時,返回0,失敗是返回錯誤碼。這個和其它的系統函數失敗返回-1,由errno返回錯誤碼不大一樣。
一個簡單的樣本如下:
#include <stdio.h>
#include
<string.h>
#include
<stdlib.h>
#include
<pthread.h>
#include
<unistd.h>
void* thr_fn(void *arg)
{
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", arg, (unsigned
int)pid, (unsigned
int)tid, (unsigned
int)tid);
return NULL;
}
int main(void)
{
pthread_t ntid = {0};
int err = pthread_create(&ntid, NULL, thr_fn, (void*)"new thread: ");
if (err != 0)
{
fprintf(stderr, "can't create thread: %s\n", strerror(err));
exit(1);
}
void *tret;
pthread_join(ntid, &tret);
printf("thread exited\n");
return 0;
}
當我們編譯這段程式時,會發現如下錯誤:
tianfang@linux-k36c:/mnt/share/test> make
make: Warning: File `Makefile' has modification time 0.48 s in the future
g++ -o run main.o
main.o: In function `main':
/mnt/share/test/main.cpp:20: undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
make: *** [run] Error 1
tianfang@linux-k36c:/mnt/share/test>
這是一個連結錯誤,它的意思是找不到pthread_create函數的定義。這是因為在Linux上線程函數位於libpthread共用庫中,因此在編譯時間要加上-lpthread選項。
等待線程結束
有的時候,我們需要等待線程結束,此時才能繼續執行後面的事情。拿前面的例子來說,如果不等待線程結束,則主線程(main函數)會直接往下執行導致程式退出,線程函數都無法得到執行。
pthread庫中等待線程結束的函數是pthread_join,它的聲明如下:
#include
<pthread.h>
int pthread_join(pthread_t thread, void **retval);
它有兩個參數,第一個參數是線程標識符,第二個參數是個出參,使用者擷取線程函數的傳回值。pthread_join在前面的例子中已經有示範,這裡就不再介紹了。
終止線程
終止線程的方法一般來說有如下幾種:
- 線程函數調用return終止自己
- 線程調用pthread_exit終止自己
- 線程可以調用pthread_cancel終止同一進程中的其它線程
其中,方法1和方法2都是通過結束線程函數來實現主動退出的,它們是比較常用的方法。方法3則是一種強制終止的做法,這個方法一個問題就是導致對象的解構函式可能無法執行,會出現資源泄漏,並不推薦用這種方法。