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 #
}

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

 

 

 

運行結果如下:

 

本教程示例代碼下載請點此鏈接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章