概述
每個進程都擁有自己的數據段、代碼段和堆棧段,這就造成進程在進行創建、切換、撤銷操作時,需要較大的系統開銷。爲了減少系統開銷,從進程中演化出了線程。爲了讓進程完成一定的工作,進程必須至少包含一個線程。線程存在於進程中,共享進程的資源。更多詳情,請看《進程和線程的區別與聯繫》。
就像每個進程都有一個進程號一樣,每個線程也有一個線程號。進程號在整個系統中是唯一的,但線程號不同,線程號只在它所屬的進程環境中有效。進程號用 pid_t 數據類型表示,是一個非負整數。線程號則用 pthread_t 數據類型來表示,Linux 使用無符號長整數表示。有的系統在實現 pthread_t 的時候,用一個結構體來表示,所以在可移植的操作系統實現不能把它做爲整數處理。
線程的常用函數
1)獲取線程號
所需頭文件:
#include <pthread.h>
pthread_t pthread_self(void);
功能:
獲取線程號。
參數:
無
返回值:
調用線程的線程 ID 。
2)線程號的比較
所需頭文件:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
功能:
判斷線程號 t1 和 t2 是否相等。爲了方便移植,儘量使用函數來比較線程 ID。
參數:
t1,t2:待判斷的線程號。
返回值:
相等: 非 0
不相等:0
示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int main(int argc, char *argv[])
{
pthread_t thread_id;
thread_id = pthread_self(); // 返回調用線程的線程ID
printf("Thread ID = %lu \n",thread_id);
if( 0 != pthread_equal( thread_id, pthread_self() ) ){
printf("Equal!\n");
}else{
printf("Not equal!\n");
}
return 0;
}
線程函數的程序在 pthread 庫中,故鏈接時要加上參數 -lpthread。
運行結果如下:
3)線程的創建
所需頭文件:
#include <pthread.h>
int pthread_create( pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg );
功能:
創建一個線程。
參數:
thread:線程標識符地址。
attr:線程屬性結構體地址,通常設置爲 NULL。
start_routine:線程函數的入口地址。
arg:傳給線程函數的參數。
返回值:
成功:0
失敗:非 0
pthread_create() 創建的線程從指定的回調函數開始運行,該函數運行完後,該線程也就退出了。線程依賴進程存在的,共享進程的資源,如果創建線程的進程結束了,線程也就結束了。
示例一:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int var = 8;
void *thread_1(void *arg)
{
while(1)
{
printf("this is my new thread1: var++\n");
var++;
sleep(1);
}
return NULL;
}
void *thread_2(void * arg)
{
while(1){
printf("this is my new thread2: var = %d\n", var);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
//創建兩個線程
pthread_create(&tid1, NULL, thread_1, NULL);
pthread_create(&tid2, NULL, thread_2, NULL);
while(1){
printf("the main thread: var = %d\n", var);
sleep(1);
}
return 0;
}
運行結果如下:
示例二:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// 回調函數
void *thread_fun(void * arg)
{
sleep(1);
int num = *( (int *)arg );
printf("int the new thread: num = %d\n", num);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid;
int test = 100;
// 創建線程, 把 &test 傳給回調函數 thread_fun()
pthread_create(&tid, NULL, thread_fun, (void *)&test);
while(1);
return 0;
}
運行結果如下:
4)回收線程資源
所需頭文件:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:
等待線程結束(此函數會阻塞),並回收線程資源,類似進程的 wait() 函數。如果線程已經結束,那麼該函數會立即返回。
參數:
thread:被等待的線程號。
retval:用來存儲線程退出狀態的指針的地址。
返回值:
成功:0
失敗:非 0
示例代碼如下:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thead(void *arg)
{
static int num = 123; //靜態變量
printf("after 2 seceonds, thread will return\n");
sleep(2);
return #
}
int main(int argc, char *argv[])
{
pthread_t tid;
int ret = 0;
void *value = NULL;
// 創建線程
ret = pthread_create(&tid, NULL, thead, NULL);
if(ret != 0){ //創建失敗
perror("pthread_create");
}
// 等待線程號爲 tid 的線程,如果此線程結束就回收其資源
// &value保存線程退出的返回值
pthread_join(tid, &value);
printf("value = %d\n", *( (int *)value ) );
return 0;
}
運行結果如下:
創建一個線程後應回收其資源,但使用 pthread_join() 函數會使調用者阻塞,Linux 還提供了非阻塞函數 pthread_detach() 來回收線程的資源。
所需頭文件:
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:
使調用線程與當前進程分離,分離後不代表此線程不依賴與當前進程,線程分離的目的是將線程資源的回收工作交由系統自動來完成,也就是說當被分離的線程結束之後,系統會自動回收它的資源。所以,此函數不會阻塞。
參數:
thread:線程號。
返回值:
成功:0
失敗:非 0
注意,調用 pthread_detach() 後再調用 pthread_join() , pthread_join() 會立馬返回,調用失敗。
示例代碼如下:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thead(void *arg)
{
int i;
for(i=0; i<5; i++)
{
printf("I am runing\n");
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret = 0;
pthread_t tid;
ret = pthread_create(&tid, NULL, thead, NULL);
if(ret!=0){
perror("pthread_create");
}
pthread_detach(tid); // 線程分離,不阻塞
// 立馬返回,調用失敗
int flag = pthread_join(tid, NULL);
if(flag != 0){
printf("join not working\n");
}
printf("after join\n");
sleep(3);
printf("I am leaving\n");
return 0;
}
運行結果如下:
5)線程退出
在進程中我們可以調用 exit() 函數或 _exit() 函數來結束進程,在一個線程中我們可以通過 pthread_exit() 在不終止整個進程的情況下停止它的控制流。
所需頭文件:
#include <pthread.h>
void pthread_exit(void *retval);
功能:
退出調用線程。一個進程中的多個線程是共享該進程的數據段,因此,通常線程退出後所佔用的資源並不會釋放。
參數:
retval:存儲線程退出狀態的指針。
返回值:
無
示例代碼如下:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thread(void *arg)
{
static int num = 123; //靜態變量
int i = 0;
while(1)
{
printf("I am runing\n");
sleep(1);
i++;
if(i==3)
{
pthread_exit( (void *)&num );
// return #
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret = 0;
pthread_t tid;
void *value = NULL;
ret = pthread_create(&tid, NULL, thread, NULL);
if(ret!=0){
perror("pthread_create");
}
pthread_join(tid, &value);
printf("value = %d\n", *(int *)value );
return 0;
}
運行結果如下: