天天看點

unix環境進階程式設計之線程篇(一)

這篇文章先講一些線程辨別和建立,會在下一篇文章中介紹線程同步。

本章介紹線程篇,第一篇先着重講線程辨別,建立,終止以及一些需要注意的東西,後續繼續更新線程同步。

一、線程辨別

就像每個程序擁有一個自己的程序ID一樣,每個線程也擁有自己的一個線程ID。程序ID在整個系統中是唯一的,但是線程ID不同,它隻在它所屬的程序環境中有效。程序id有個資料類型pid_t來表示,他是個非負整數。線程id則用pthread_t資料類型來表示,但是這個資料類型在移植的時候不能

簡單的當成整數來處理,是以需要使用函數來判定兩個線程id是否相等;

#include <pthread.h>

int pthread_equal(pthread_t tid1,pthread_t tid2);//若相等則傳回非0值,否則傳回0

線程需要擷取自己的id可使用pthread_self()函數擷取

#include <pthread.h>

pthread_t pthread_self(void);

二、線程建立

新增線程的建立可以調用pthread_create函數建立;

#include <pthread.h>

int pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(*start)(void),void *arg);//成功傳回0,失敗傳回錯誤編号

當pthread_create成功傳回時,tid指向的記憶體單元被設定為新建立線程的線程id。

attr參數用于定制各種不同的線程屬性,可設定為NULL,建立線程時會使用預設的屬性。

新建立的線程從start函數的位址開始運作,該函數隻有一個無類型的指針參數arg,如果需要向start函數傳遞多個參數,需要把這些參數放到一個結構中,然後把這個結構的位址作為arg參數傳入。

線程建立後并不能保證哪個線程先運作,可能是新建立的線程,也可能是調用線程。新建立的線程可以通路程序的位址空間,并且繼承調用線程

的浮點環境和信号屏蔽字,但是該線程的未決信号集被清除。

例子:

#include <pthread.h>
#include <stdio.h>
	
pthread_t ntid;
	
void printids(const char* src)
{
	pid_t pid;
	pthread_t tid;
		
	pid = getpid();
	tid = pthread_self();
		
	pthread("%s pid:%u, tid:%u (0x%x)\n",src,(unsigned int pid),(unsigned int)tid,(unsigned int)tid);
}
	
void *thr_fn(*arg)
{
	printids("new thread:");
	return (void*)0;
}
	
int main()
{
	int err;
	err = pthread_create(&ntid,NULL,thr_fn,NULL);
	if(0 != err)
		printf("create pthread err:%s\n",strerror(err));
	printids("main thread:");
	sleep(1);
	exit(0);
}
           

這個例子要說明兩個問題:

1、要處理主線程和新線程之間的競争,是以主線程要用sleep,否則,因主線程和新線程誰先運作的不确定性,新線程可能在運作之前

主線程已經退出了。導緻新線程無法運作。

2、新線程擷取自己的id要使用pthread_self()函數擷取,不能從全局變量ntid得到,因為新線程可能在主線程之前運作,這樣直接

擷取的ntid是不正确的。

linux下線程id比較合理,但是程序id的擷取卻并不比對,這和linux線程的實作有關,linux使用clone系統調用來實作pthread_create,

clone系統調用建立子程序,這個子程序可以共享父程序一定數量的執行環境,而且這個數量可配。

三、線程終止

程序的任意線程調用了exit,_Exit,_exit,那麼整個程序就會終止。以此類似,如果信号的預設動作是終止程序,那麼将該信号發

送到線程也會終止整個程序。

單個線程有三種方式退出,在不終止整個程序的情況下停止它的控制流

1、線程訓示從啟動曆程中傳回,傳回值是線程的終止碼

2、線程可以被統一程序中的其他線程終止取消

3、線程調用pthread_exit。

#include <pthread>

void pthread_exit(void *rval_ptr);

rval_ptr是無類型的,程序中的其他線程可以調用pthread_join函數通路擷取到這個值

int pthread_join(pthread_t thread,void *rval_ptr);//成功傳回0,出錯傳回錯誤編号

調用者将一直阻塞,直到指定線程調用pthread_exit,或傳回,或被取消。如果線程被取消,rval_ptr值為PTHREAD_CANCELED,線程可以設定為分離狀态,如果線程已經設定為分離狀态,那麼調用pthread_join就會失敗,傳回EINVAL.

注意pthread_exit調用的傳回值所指向的記憶體線上程終止後要合法的,否則主線程去通路這個指針指向記憶體會出現非法通路的問題。

例子比較少,會在後續持續更新,希望繼續關注!

繼續閱讀