Linux系統程式設計——多執行緒實現多工
概述
每個程序都擁有自己的資料段、程式碼段和堆疊段,這就造成程序在進行建立、切換、撤銷操作時,需要較大的系統開銷。為了減少系統開銷,從程序中演化出了執行緒。為了讓程序完成一定的工作,程序必須至少包含一個執行緒。執行緒存在於程序中,共享程序的資源。更多詳情,請看《程序和執行緒的區別與聯絡》。
就像每個程序都有一個程序號一樣,每個執行緒也有一個執行緒號。程序號在整個系統中是唯一的,但執行緒號不同,執行緒號只在它所屬的程序環境中有效。程序號用 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 (void *)#
}
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;
}
執行結果如下: