1. 程式人生 > >Linux系統程式設計——多執行緒實現多工

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 *)&num;
}

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 &num;
		}
	}
	
	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;
}

執行結果如下: