1.引言
目前,許多流行的多任務作業系統都提供線程機制,線程就是程式中的 單個順序控制流。利用多線程進行程式設計,就是将一個程式(程序)的任務劃分為執行的多個部分(線程) ,每一個線程為一個順序的單控制流,而所有線程都是并發執行的,這樣,多線程程式就可以實作并行計算,高效利用多處理器。線程可分為使用者級線程和核心級線 程兩種基本類型。使用者級線程不需要核心支援,可以在使用者程式中實作,線程排程、同步與互斥都需要使用者程式自己完成。核心級線程需要核心參與,由核心完成線 程排程并提供相應的系統調用,使用者程式可以通過這些接口函數對線程進行一定的控制和管理。Linux作業系統提供了LinuxThreads庫,它是符合POSIX1003.1c标準的核心級多線程函數庫。在linuxthreads庫中提供了一些多線程程式設計的關鍵函數,在多線程程式設計時應包括pthread.h檔案。
2.LinuxThread中的關鍵庫函數
2.1線程的建立和終止
int pthread_create(pthread_t * pthread,const pthread_attr_t *attr,void *(*start_routine(*void)),void *arg);調用此函數可以建立一個新的線程,新線程建立後執行start_routine 指定的程式。其中參數attr是使用者希望建立線程的屬性,當為NULL時表示以預設的屬性建立線程。arg是向start_routine 傳遞的參數。當成功建立一個新的線程時,系統會自動為新線程配置設定一個線程ID号,并通過pthread 傳回給調用者。
void pthread_exit(void *value_ptr);調用該函數可以退出線程,參數value_ptr是一個指向傳回狀态值的指針。
2.2線程控制函數
pthread_self(void);為了區分線程,線上程建立時系統為其配置設定一個唯一的ID号,由pthread_create()傳回給調用者,也可以通過pthread_self()擷取自己的線程ID。
Int pthread_join (pthread- t thread , void * *status);這個函數的作用是等待一個線程的結束。調用pthread_join()的線程将被挂起直到線程ID為參數thread 指定的線程終止。
int pthread_detach(pthread_t pthread);參數pthread代表的線程一旦終止,立即釋放調該線程占有的所有資源。
2.3線程間的互斥
互斥量和臨界區類似,隻有擁有互斥量的線程才具有通路資源的權限, 由于互斥對象隻有一個,這就決定了任何情況下共享資源(代碼或變量)都不會被多個線程同時通路。使用互斥不僅能夠在同一應用程式的不同線程中實作資源的安 全共享,而且可以在不同應用程式的線程之間實作對資源的安全共享。Linux中通過pthread_mutex_t來定義互斥體機制完成互斥操作。具體的操作函數如下
pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);初使化一個互斥體變量mutex,參數attr表示按照attr屬性建立互斥體變量mutex,如果參數attr為NULL,則以預設的方式建立。
pthread_mutex_lock(pthread_mutex_t *mutex);給一個互斥體變量上鎖,如果mutex指定的互斥體已經被鎖住,則調用線程将被阻塞直到擁有mutex的線程對mutex解鎖為止。
Pthread_mutex_unlock(pthread_mutex_t *mutex);對參數mutex指定的互斥體變量解鎖。
2.4線程間的同步
同步就是線程等待某一個事件的發生,當等待的事件發生時,被等待的線程和事件一起繼續執行。如果等待的事件未到達則挂起。在linux作業系統中是通過條件變量來實作同步的。
Pthread_cond_init(pthread_cond_t *cond,const pthread_cond_t *attr);這個函數按參數attr指定的屬性初使化一個條件變量cond。
Pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);等待一個事件(條件變量)的發生,發出調用的線程自動阻塞,直到相應的條件變量被置1。等待狀态的線程不占用CPU時間。
pthread_cond_signal(pthread_cond_t *cond);解除一個等待參數cond指定的條件變量的線程的阻塞狀态。
3.多線程程式設計的應用執行個體
在這裡利用多線程技術實作生産者和消費者問題,生産者線程向一緩沖區中寫資料, 消費者線程從緩沖區中讀取資料,由于生産者線程和消費者線程共享同一緩沖區,為了正确讀寫資料,在使用緩沖隊列時必須保持互斥。生産者線程和消費者線程必 須滿足:生産者寫入緩沖區的數目不能超過緩沖區容量,消費者讀取的數目不能超過生産者寫入的數目。在程式中使用了一個小技巧來判斷緩沖區是空還是滿。在初 始化時讀指針和寫指針為0;如果讀指針等于寫指針,則緩沖區是空的;如果(寫指針+ 1) % N 等于讀指針,則緩沖區是滿的,%表示取餘數,這時實際上有一個單元空出未用。下面是完整的程式段和注釋。
#include<stdio.h>
#include<pthread.h>
#define BUFFER_SIZE 8
struct prodcons {
int buffer[BUFFER_SIZE];
pthread_mutex_t lock; //互斥LOCK
int readpos , writepos;
pthread_cond_t notempty; //緩沖區非空條件判斷
pthread_cond_t notfull; //緩沖區未滿條件判斷
};
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;
}
上 面的例子中,生産者負責将1到1000的整數寫入緩沖區,而消費者負責從同一個緩沖區中讀取寫入的整數并列印出來。因為生産者和消費者是兩個同時運作的線 程,并且要使用同一個緩沖區進行資料交換,是以必須利用一種機制進行同步。通過上面的例子我們可以看到,多線程的最大好處是,除堆棧之外,幾乎所有的資料 均是共享的,是以線程間的通訊效率很高;缺點:因為共享所有資料,進而非常容易導緻線程之間互相破壞資料,這一點在程式設計時必須注意。
4.結束語
Linux中基于POSIX标準的很好的支援了多線程技術,它減少了程式并發執行時的系統開銷,提高了計算機的工作效率。在具體程式設計過程中要了解線程的間的關系,還要考慮共享資料的保護,在互斥和同步機制下保證代碼的高效運作,程式編譯時用gcc -D –REENTRANT -libpthread.xx.so filename.c編譯。