1.引言
目前,許多流行的多任務(wù)操作系統(tǒng)都提供線程機(jī)制,線程就是程序中的 單個(gè)順序控制流。利用多線程進(jìn)行程序設(shè)計(jì),就是將一個(gè)程序(進(jìn)程)的任務(wù)劃分為執(zhí)行的多個(gè)部分(線程) ,每一個(gè)線程為一個(gè)順序的單控制流,而所有線程都是并發(fā)執(zhí)行的,這樣,多線程程序就可以實(shí)現(xiàn)并行計(jì)算,高效利用多處理器。線程可分為用戶級(jí)線程和內(nèi)核級(jí)線 程兩種基本類型。用戶級(jí)線程不需要內(nèi)核支持,可以在用戶程序中實(shí)現(xiàn),線程調(diào)度、同步與互斥都需要用戶程序自己完成。內(nèi)核級(jí)線程需要內(nèi)核參與,由內(nèi)核完成線 程調(diào)度并提供相應(yīng)的系統(tǒng)調(diào)用,用戶程序可以通過(guò)這些接口函數(shù)對(duì)線程進(jìn)行一定的控制和管理。Linux操作系統(tǒng)提供了LinuxThreads庫(kù),它是符合POSIX1003.1c標(biāo)準(zhǔn)的內(nèi)核級(jí)多線程函數(shù)庫(kù)。在linuxthreads庫(kù)中提供了一些多線程編程的關(guān)鍵函數(shù),在多線程編程時(shí)應(yīng)包括pthread.h文件。
2.LinuxThread中的關(guān)鍵庫(kù)函數(shù)
2.1線程的創(chuàng)建和終止
int pthread_create(pthread_t * pthread,const pthread_attr_t *attr,void *(*start_routine(*void)),void *arg);調(diào)用此函數(shù)可以創(chuàng)建一個(gè)新的線程,新線程創(chuàng)建后執(zhí)行start_routine 指定的程序。其中參數(shù)attr是用戶希望創(chuàng)建線程的屬性,當(dāng)為NULL時(shí)表示以默認(rèn)的屬性創(chuàng)建線程。arg是向start_routine 傳遞的參數(shù)。當(dāng)成功創(chuàng)建一個(gè)新的線程時(shí),系統(tǒng)會(huì)自動(dòng)為新線程分配一個(gè)線程ID號(hào),并通過(guò)pthread 返回給調(diào)用者。
void pthread_exit(void *value_ptr);調(diào)用該函數(shù)可以退出線程,參數(shù)value_ptr是一個(gè)指向返回狀態(tài)值的指針。
2.2線程控制函數(shù)
pthread_self(void);為了區(qū)分線程,在線程創(chuàng)建時(shí)系統(tǒng)為其分配一個(gè)唯一的ID號(hào),由pthread_create()返回給調(diào)用者,也可以通過(guò)pthread_self()獲取自己的線程ID。
Int pthread_join (pthread- t thread , void * *status);這個(gè)函數(shù)的作用是等待一個(gè)線程的結(jié)束。調(diào)用pthread_join()的線程將被掛起直到線程ID為參數(shù)thread 指定的線程終止。
int pthread_detach(pthread_t pthread);參數(shù)pthread代表的線程一旦終止,立即釋放調(diào)該線程占有的所有資源。
2.3線程間的互斥
互斥量和臨界區(qū)類似,只有擁有互斥量的線程才具有訪問(wèn)資源的權(quán)限, 由于互斥對(duì)象只有一個(gè),這就決定了任何情況下共享資源(代碼或變量)都不會(huì)被多個(gè)線程同時(shí)訪問(wèn)。使用互斥不僅能夠在同一應(yīng)用程序的不同線程中實(shí)現(xiàn)資源的安 全共享,而且可以在不同應(yīng)用程序的線程之間實(shí)現(xiàn)對(duì)資源的安全共享。Linux中通過(guò)pthread_mutex_t來(lái)定義互斥體機(jī)制完成互斥操作。具體的操作函數(shù)如下
pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);初使化一個(gè)互斥體變量mutex,參數(shù)attr表示按照attr屬性創(chuàng)建互斥體變量mutex,如果參數(shù)attr為NULL,則以默認(rèn)的方式創(chuàng)建。
pthread_mutex_lock(pthread_mutex_t *mutex);給一個(gè)互斥體變量上鎖,如果mutex指定的互斥體已經(jīng)被鎖住,則調(diào)用線程將被阻塞直到擁有mutex的線程對(duì)mutex解鎖為止。
Pthread_mutex_unlock(pthread_mutex_t *mutex);對(duì)參數(shù)mutex指定的互斥體變量解鎖。
2.4線程間的同步
同步就是線程等待某一個(gè)事件的發(fā)生,當(dāng)?shù)却氖录l(fā)生時(shí),被等待的線程和事件一起繼續(xù)執(zhí)行。如果等待的事件未到達(dá)則掛起。在linux操作系統(tǒng)中是通過(guò)條件變量來(lái)實(shí)現(xiàn)同步的。
Pthread_cond_init(pthread_cond_t *cond,const pthread_cond_t *attr);這個(gè)函數(shù)按參數(shù)attr指定的屬性初使化一個(gè)條件變量cond。
Pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);等待一個(gè)事件(條件變量)的發(fā)生,發(fā)出調(diào)用的線程自動(dòng)阻塞,直到相應(yīng)的條件變量被置1。等待狀態(tài)的線程不占用CPU時(shí)間。
pthread_cond_signal(pthread_cond_t *cond);解除一個(gè)等待參數(shù)cond指定的條件變量的線程的阻塞狀態(tài)。
3.多線程編程的應(yīng)用實(shí)例
在這里利用多線程技術(shù)實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者問(wèn)題,生產(chǎn)者線程向一緩沖區(qū)中寫(xiě)數(shù)據(jù), 消費(fèi)者線程從緩沖區(qū)中讀取數(shù)據(jù),由于生產(chǎn)者線程和消費(fèi)者線程共享同一緩沖區(qū),為了正確讀寫(xiě)數(shù)據(jù),在使用緩沖隊(duì)列時(shí)必須保持互斥。生產(chǎn)者線程和消費(fèi)者線程必 須滿足:生產(chǎn)者寫(xiě)入緩沖區(qū)的數(shù)目不能超過(guò)緩沖區(qū)容量,消費(fèi)者讀取的數(shù)目不能超過(guò)生產(chǎn)者寫(xiě)入的數(shù)目。在程序中使用了一個(gè)小技巧來(lái)判斷緩沖區(qū)是空還是滿。在初 始化時(shí)讀指針和寫(xiě)指針為0;如果讀指針等于寫(xiě)指針,則緩沖區(qū)是空的;如果(寫(xiě)指針+ 1) % N 等于讀指針,則緩沖區(qū)是滿的,%表示取余數(shù),這時(shí)實(shí)際上有一個(gè)單元空出未用。下面是完整的程序段和注釋。
#include
#include
#define BUFFER_SIZE 8
struct prodcons {
int buffer[BUFFER_SIZE];
pthread_mutex_t lock;????? //互斥LOCK
int readpos , writepos;
pthread_cond_t notempty;?? //緩沖區(qū)非空條件判斷
pthread_cond_t notfull;??? //緩沖區(qū)未滿條件判斷
};
void init(struct prodcons * b){
pthread_mutex_init(&b->lock,NULL);
pthread_cond_init(&b->notempty,NULL);
pthread_cond_init(&b->notfull,NULL);
b->readpos=0;
b->writepos=0;
}
void put(struct prodcons* b,int data){
pthread-_mutex_lock(&b->lock);
if((b->writepos + 1) % BUFFER_SIZE == b->readpos)
{
pthread_cond_wait(&b->notfull, &b->lock) ;
}
b->buffer[b->writepos]=data;
b->writepos++;
if(b->writepos >= BUFFER_SIZE)
b->writepos=0;
pthread_cond_signal(&b->notempty);
pthread_mutex_unlock(&b->lock);
}
int get(struct prodcons *b){
int data;
pthread_mutex_lock(&b->lock);
if(b->writepos == b->readpos)
{
pthread_cond _wait(&b->notempty, &b->lock);
}
data = b->buffer[b->readpos];
b->readpos++;
if(b->readpos >= BUFFER_SIZE)
b->readpos=0;
pthread_cond_signal(&b->notfull);
pthread_mutex_unlock(&b->lock);
return data;
}
#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
int n;
for(n = 0; n < 10000; n++)
{
printf("%d \n", n) ;
put(&buffer, n);
}
put(&buffer, OVER);
return NULL;
}
void *consumer(void * data)
{
int d;
while(1)
{
d = get(&buffer);
if(d == OVER)
break;
printf("%d\n", d);
}
return NULL;
}
int main(void)
{
pthread_t th_a, th_b;
void *retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, 0);
& nbsp;?? pthread_create(&th_b, NULL, consumer, 0);
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
return 0;
}
上 面的例子中,生產(chǎn)者負(fù)責(zé)將1到1000的整數(shù)寫(xiě)入緩沖區(qū),而消費(fèi)者負(fù)責(zé)從同一個(gè)緩沖區(qū)中讀取寫(xiě)入的整數(shù)并打印出來(lái)。因?yàn)樯a(chǎn)者和消費(fèi)者是兩個(gè)同時(shí)運(yùn)行的線 程,并且要使用同一個(gè)緩沖區(qū)進(jìn)行數(shù)據(jù)交換,因此必須利用一種機(jī)制進(jìn)行同步。通過(guò)上面的例子我們可以看到,多線程的最大好處是,除堆棧之外,幾乎所有的數(shù)據(jù) 均是共享的,因此線程間的通訊效率很高;缺點(diǎn):因?yàn)楣蚕硭袛?shù)據(jù),從而非常容易導(dǎo)致線程之間互相破壞數(shù)據(jù),這一點(diǎn)在編程時(shí)必須注意。
4.結(jié)束語(yǔ)
Linux中基于POSIX標(biāo)準(zhǔn)的很好的支持了多線程技術(shù),它減少了程序并發(fā)執(zhí)行時(shí)的系統(tǒng)開(kāi)銷,提高了計(jì)算機(jī)的工作效率。在具體編程過(guò)程中要了解線程的間的關(guān)系,還要考慮共享數(shù)據(jù)的保護(hù),在互斥和同步機(jī)制下保證代碼的高效運(yùn)行,程序編譯時(shí)用gcc -D –REENTRANT -libpthread.xx.so filename.c編譯。
評(píng)論
查看更多