線程的創建和終止

一. 線程的創建

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
                   const pthread_attr_t *restrict attr,
                   void *(*start rtn)(void*), void *restrict arg);
返回:成功返回0,否則返回錯誤編號
參數:tidp:線程標識符指針;(存放所創建線程的標識符的地址)

            attr:線程屬性指針;

            start_rtn:線程運行函數的起始地址;

            arg:傳遞給線程運行函數的參數;

注:不能保證新線程和調用線程的執行順序(執行順序由調度算法決定)

下面給出一個具體的案例來說明線程的創建過程。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

//定義線程運行函數
void* th_fn(void *arg)
{
	int distance = (int)arg;
	int i;
	for(i = 1; i <= distance; ++i)
	{
		printf("%lx run %d\n", pthread_self(), i);
		int time = (int)(drand48() * 100000);// 隨機睡眠一定的時間
		usleep(time); // 微妙
	}

//	return (void*)0;
	return (void*)distance;
}

int main(void)
{
	int err;
	pthread_t rabbit, turtle; // 定義線程標識符

	// 創建rabbit線程
	// 第二個參數是線程的屬性
	// 第三個參數是線程運行函數的起始地址
	if((err = pthread_create(&rabbit, NULL,
						th_fn, (void*)50)) != 0)
	{
		perror("pthread_create error");
	}

	//創建turtle線程
	if((err == pthread_create(&turtle, NULL,
						th_fn, (void*)50)) != 0)
	{
		perror("pthread_create error");
	}
	//主控線程調用pthread_join(),自己會阻塞
	//直到rabbit線程和turtle線程結束方可運行
//	pthread_join(rabbit, NULL);
//	pthread_join(turtle, NULL);	
	//sleep(10); // 主控線程運a行
	
	int result;
	pthread_join(rabbit, (void*)&result);
	printf("rabbit race distance is: %d\n", result);
	pthread_join(turtle, (void*)&result);
	printf("turtle race distance is: %d\n", result);
	printf("race is finished\n");
	
	printf("control thread id: %lx\n", pthread_self());
	printf("finished\n");

	return 0;
}
該案例是一個簡易的龜兔賽跑模型。

首先創建兩個線程rabbit和turtle。在pthread_create中,第四個參數時傳遞給線程運行函數得參數。第三個參數是線程運行函數th_fn,這裏我們打印出二者所跑的路程,每跑一步都進行一次睡眠。

說明:a. 在程序中,主控線程調用pthread_join()函數後自己會阻塞,rabbit線程和turtle線程運行結束主控線程方可運行;

             b. pthread_join函數中的第二個參數存儲的是線程運行函數的返回結果;


當然,如果在線程運行函數中我們想要輸出更多的內容,也就是參數中包含更多的內容,我們可以將這些內容封裝在一個結構體變量中,然後傳遞給函數,下面的例子對上例做了稍微的改變。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct
{
	char	name[20]; //存儲線程的名字
	int		time; //線程睡眠時間
	int 	start;
	int 	end;
}RaceArg;

void* th_fn(void *arg)
{
	RaceArg *r = (RaceArg*)arg;
	int i = r->start;
	for(; i <= r->end; ++i)
	{
		printf("%s(%lx) running %d\n",
				r->name, pthread_self(), i);
		usleep(r->time);
	}

	//return (void*)0;//主動終止 
	//pthread_exit((void*)0);
	
	return (void*)(r->end - r->start);
}

int main(void)
{
	int err;
	pthread_t rabbit, turtle;

	RaceArg r_a = {"rabbit", (int)(drand48()*100000000), 20, 50};
	RaceArg t_a = {"turtle", (int)(drand48()*100000000), 10, 60};
	
	if((err = pthread_create(&rabbit, NULL,
						th_fn, (void*)&r_a)) != 0)
	{
		perror("pthread_create error");
	}
	if((err = pthread_create(&turtle, NULL,
						th_fn, (void*)&t_a)) != 0)
	{
		perror("pthread_create error");
	}
	
	//主控線程調用pthrea_join,自己阻塞
	//等待其他線程運行結束自己再運行
//	pthread_join(rabbit, NULL);
//	pthread_join(turtle, NULL);
	
	int result;
	pthread_join(rabbit, (void*)&result);
	printf("rabbit race distance is %d\n", result);
	pthread_join(turtle, (void*)&result);
	printf("turtle race distance is %d\n", result);
	printf("race finished\n");

	printf("control thread id: %lx\n", pthread_self());
	printf("finished.\n");

	return 0;
}
在該例子中,我們將線程的名字、每一步的睡眠時間、起始和終止點都封裝在了一個結構體變量中,在線程運行函數中,我們又將該結構體變量作爲參數傳遞給線程運行函數,並將其輸出,最後線程運行函數返回的是總的路程。

注意:在pthread_create中,第四個參數就不再是一個數了,而是一個包含很多內容的結構體變量。同樣,這裏需要主控線程進行阻塞。

問題:那麼在上述的案例中,兩個線程之間會公用一些資源,這期間會不會互相干擾?



我們知道,每個線程都有自己獨立的棧空間,像線程運行函數中的一些局部變量就存儲在這些空間中。當然不同的線程也有一些共享的資源,比如上圖中的數據段部分,該空間存儲着全局變量和靜態變量,由各個線程共享,顯然這樣是不安全的,因爲一個線程對某一個變量做了修改之後,另一個線程訪問該變量時是修改後的。因此,在多線程編程中建議儘量使用局部變量


二. 線程終止

1. 線程終止方式

  • 主動終止:線程的執行函數中調用return語句或者調用pthread_exit()函數;
  • 被動終止:線程可以被同一進程的其他線程取消,其他線程調用pthread_cancel(pthid);
#include <pthread.h>
int pthread_cancel(pthread_t pid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);
//返回值:成功返回0,否則返回錯誤編號;
函數解釋:
pthread_cancel:線程可以被同一進程的其他線程取消,tid爲被終止的線程標識符;
pthread_exit:1)retval:pthread_exit調用者線程的返回值,可由其他函數和pthread_join來檢測獲取;
                       2)線程退出時使用函數pthread_exit,是線程的主動行爲;
                       3)由一個線程中的多個線程共享數據段,因此通常在線程退出後,退出線程所佔用的資源並不會隨線程的結束而釋放。所有需                             要pthread_join函數來等待線程結束,類似於wait系統調用;
pthread_join:1)th:被等待線程的標識符;
                       2)thread_return:用戶定義指針,用來存儲被等待線程的返回值;

補充:pthread_join函數的作用?
pthread_join使一個線程等待另一個線程結束。
代碼中如果沒有pthread_join,主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join後,主線程會一直等待,知道等待的線程結束自己才結束,使創建的線程有機會執行。

下面給出一個簡單示例:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

typedef struct
{
	int		d1;
	int		d2;
}Arg;

void* th_fn(void *arg)
{
	Arg *r = (Arg*)arg;

	//return (void*)(r->d1 + r->d2);]
	return (void*)r;
}

int main(void)
{
	int err;
	pthread_t th;
	Arg r = {20, 50};

	if((err = pthread_create(&th, NULL,
					th_fn, (void*)&r)) != 0)
	{
		perror("pthread_create error");
	}

	/*
	int *result;
	pthread_join(th, (void**)&result);//第二個參數獲得子線程的返回結果
	printf("result is %d\n", (int)result);//這裏做了強制轉換
	*/
	
	/*
	int result;
	pthread_join(th, void(*)&result);
	printf("result is %d\n", result);//這裏就不需要強制轉換
	*/

	int *result;
	pthread_join(th, (void**)&result);
	printf("result is %d\n",
					((Arg*)result)->d1 + ((Arg*)result)->d2);
	
	
	return 0;
}
該示例就是一個簡單的加法,不再作詳細的解釋了。
在上面線程創建的第二個程序中,在線程運行函數中分別有return和pthread_exit主動退出的說明。


發佈了70 篇原創文章 · 獲贊 29 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章