目前Linux中最流行的線程機(jī)制為L(zhǎng)inuxThreads,所采用的就是線程-進(jìn)程“一對(duì)一”模型,調(diào)度交給核心,而在用戶級(jí)實(shí)現(xiàn)一個(gè)包括信號(hào)處理在內(nèi)的線程管理機(jī)制。LinuxThreads由Xavier Leroy (Xavier.Leroy@inria.fr)負(fù)責(zé)開發(fā)完成,并已綁定在GLIBC中發(fā)行,它實(shí)現(xiàn)了一種BiCapitalized面向Linux的Posix 1003.1c “pthread”標(biāo)準(zhǔn)接口。Linuxthread可以支持Intel、Alpha、MIPS等平臺(tái)上的多處理器系統(tǒng)。
按照POSIX 1003.1c 標(biāo)準(zhǔn)編寫的程序與Linuxthread 庫(kù)相鏈接即可支持Linux平臺(tái)上的多線程,在程序中需包含頭文件pthread. h,在編譯鏈接時(shí)使用命令:
gcc -D -REENTRANT -lpthread xxx. c
其中-REENTRANT宏使得相關(guān)庫(kù)函數(shù)(如stdio.h、errno.h中函數(shù)) 是可重入的、線程安全的(thread-safe),-lpthread則意味著鏈接庫(kù)目錄下的libpthread.a或libpthread.so文件。使用Linuxthread庫(kù)需要2.0以上版本的Linux內(nèi)核及相應(yīng)版本的C庫(kù)(libc 5.2.18、libc 5.4.12、libc 6)。
2.“線程”控制
線程創(chuàng)建
進(jìn)程被創(chuàng)建時(shí),系統(tǒng)會(huì)為其創(chuàng)建一個(gè)主線程,而要在進(jìn)程中創(chuàng)建新的線程,則可以調(diào)用pthread_create:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(start_routine)(void*), void *arg);
start_routine為新線程的入口函數(shù),arg為傳遞給start_routine的參數(shù)。
每個(gè)線程都有自己的線程ID,以便在進(jìn)程內(nèi)區(qū)分。線程ID在pthread_create調(diào)用時(shí)回返給創(chuàng)建線程的調(diào)用者;一個(gè)線程也可以在創(chuàng)建后使用pthread_self()調(diào)用獲取自己的線程ID:
pthread_self (void) ;
線程退出
線程的退出方式有三:
(1)執(zhí)行完成后隱式退出;
(2)由線程本身顯示調(diào)用pthread_exit 函數(shù)退出;
pthread_exit (void * retval) ;
(3)被其他線程用pthread_cance函數(shù)終止:
pthread_cance (pthread_t thread) ;
在某線程中調(diào)用此函數(shù),可以終止由參數(shù)thread 指定的線程。
如果一個(gè)線程要等待另一個(gè)線程的終止,可以使用pthread_join函數(shù),該函數(shù)的作用是調(diào)用pthread_join的線程將被掛起直到線程ID為參數(shù)thread的線程終止:
pthread_join (pthread_t thread, void** threadreturn);
3.線程通信
線程互斥
互斥意味著“排它”,即兩個(gè)線程不能同時(shí)進(jìn)入被互斥保護(hù)的代碼。Linux下可以通過pthread_mutex_t 定義互斥體機(jī)制完成多線程的互斥操作,該機(jī)制的作用是對(duì)某個(gè)需要互斥的部分,在進(jìn)入時(shí)先得到互斥體,如果沒有得到互斥體,表明互斥部分被其它線程擁有,此時(shí)欲獲取互斥體的線程阻塞,直到擁有該互斥體的線程完成互斥部分的操作為止。
下面的代碼實(shí)現(xiàn)了對(duì)共享全局變量x 用互斥體mutex 進(jìn)行保護(hù)的目的:
int x; // 進(jìn)程中的全局變量
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的屬性初始化互斥體變量mutex
pthread_mutex_lock(&mutex); // 給互斥體變量加鎖
… //對(duì)變量x 的操作
phtread_mutex_unlock(&mutex); // 給互斥體變量解除鎖
線程同步
同步就是線程等待某個(gè)事件的發(fā)生。只有當(dāng)?shù)却氖录l(fā)生線程才繼續(xù)執(zhí)行,否則線程掛起并放棄處理器。當(dāng)多個(gè)線程協(xié)作時(shí),相互作用的任務(wù)必須在一定的條件下同步。
Linux下的C語言編程有多種線程同步機(jī)制,最典型的是條件變量(condition variable)。pthread_cond_init用來創(chuàng)建一個(gè)條件變量,其函數(shù)原型為:
pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_wait和pthread_cond_timedwait用來等待條件變量被設(shè)置,值得注意的是這兩個(gè)等待調(diào)用需要一個(gè)已經(jīng)上鎖的互斥體mutex,這是為了防止在真正進(jìn)入等待狀態(tài)之前別的線程有可能設(shè)置該條件變量而產(chǎn)生競(jìng)爭(zhēng)。pthread_cond_wait的函數(shù)原型為:
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_broadcast用于設(shè)置條件變量,即使得事件發(fā)生,這樣等待該事件的線程將不再阻塞:
pthread_cond_broadcast (pthread_cond_t *cond) ;
pthread_cond_signal則用于解除某一個(gè)等待線程的阻塞狀態(tài):
pthread_cond_signal (pthread_cond_t *cond) ;
pthread_cond_destroy 則用于釋放一個(gè)條件變量的資源。
在頭文件semaphore.h 中定義的信號(hào)量則完成了互斥體和條件變量的封裝,按照多線程程序設(shè)計(jì)中訪問控制機(jī)制,控制對(duì)資源的同步訪問,提供程序設(shè)計(jì)人員更方便的調(diào)用接口。
sem_init(sem_t *sem, int pshared, unsigned int val);
這個(gè)函數(shù)初始化一個(gè)信號(hào)量sem 的值為val,參數(shù)pshared 是共享屬性控制,表明是否在進(jìn)程間共享。
sem_wait(sem_t *sem);
調(diào)用該函數(shù)時(shí),若sem為無狀態(tài),調(diào)用線程阻塞,等待信號(hào)量sem值增加(post )成為有信號(hào)狀態(tài);若sem為有狀態(tài),調(diào)用線程順序執(zhí)行,但信號(hào)量的值減一。
sem_post(sem_t *sem);
調(diào)用該函數(shù),信號(hào)量sem的值增加,可以從無信號(hào)狀態(tài)變?yōu)橛行盘?hào)狀態(tài)。
4.實(shí)例
下面我們還是以著名的生產(chǎn)者/消費(fèi)者問題為例來闡述Linux線程的控制和通信。一組生產(chǎn)者線程與一組消費(fèi)者線程通過緩沖區(qū)發(fā)生聯(lián)系。生產(chǎn)者線程將生產(chǎn)的產(chǎn)品送入緩沖區(qū),消費(fèi)者線程則從中取出產(chǎn)品。緩沖區(qū)有N 個(gè),是一個(gè)環(huán)形的緩沖池。
- #include <stdio.h>
- #include <pthread.h>
- #define BUFFER_SIZE 16 // 緩沖區(qū)數(shù)量
- struct prodcons
- {
-
- int buffer[BUFFER_SIZE];
- pthread_mutex_t 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);
- pthread_create(&th_b, NULL, consumer, 0);
-
- pthread_join(th_a, &retval);
- pthread_join(th_b, &retval);
- return 0;
- }
5.WIN32、VxWorks、Linux線程類比
目前為止,筆者已經(jīng)創(chuàng)作了《基于嵌入式操作系統(tǒng)VxWorks的多任務(wù)并發(fā)程序設(shè)計(jì)》(《軟件報(bào)》2006年5~12期連載)、《深入淺出Win32多線程程序設(shè)計(jì)》(天極網(wǎng)技術(shù)專題)系列,我們來找出這兩個(gè)系列文章與本文的共通點(diǎn)。
看待技術(shù)問題要瞄準(zhǔn)其本質(zhì),不管是Linux、VxWorks還是WIN32,其涉及到多線程的部分都是那些內(nèi)容,無非就是線程控制和線程通信,它們的許多函數(shù)只是名稱不同,其實(shí)質(zhì)含義是等價(jià)的,下面我們來列個(gè)三大操作系統(tǒng)共同點(diǎn)詳細(xì)表單:
事項(xiàng)
|
WIN32
|
VxWorks
|
Linux
|
線程創(chuàng)建
|
CreateThread
|
taskSpawn
|
pthread_create
|
線程終止
|
執(zhí)行完成后退出;線程自身調(diào)用ExitThread 函數(shù)即終止自己;被其他線程調(diào)用函數(shù)TerminateThread函數(shù)
|
執(zhí)行完成后退出;由線程本身調(diào)用exit退出;被其他線程調(diào)用函數(shù)taskDelete終止
|
執(zhí)行完成后退出;由線程本身調(diào)用pthread_exit退出;被其他線程調(diào)用函數(shù)pthread_cance終止
|
獲取線程ID
|
GetCurrentThreadId
|
taskIdSelf
|
pthread_self
|
創(chuàng)建互斥
|
CreateMutex
|
semMCreate
|
pthread_mutex_init
|
獲取互斥
|
WaitForSingleObject、
WaitForMultipleObjects
|
semTake
|
pthread_mutex_lock
|
釋放互斥
|
ReleaseMutex
|
semGive
|
phtread_mutex_unlock
|
創(chuàng)建信號(hào)量
|
CreateSemaphore
|
semBCreate、semCCreate
|
sem_init
|
等待信號(hào)量
|
WaitForSingleObject
|
semTake
|
sem_wait
|
釋放信號(hào)量
|
ReleaseSemaphore
|
semGive
|
sem_post
|
6.小結(jié)
本章講述了Linux下多線程的控制及線程間通信編程方法,給出了一個(gè)生產(chǎn)者/消費(fèi)者的實(shí)例,并將Linux的多線程與WIN32、VxWorks多線程進(jìn)行了類比,總結(jié)了一般規(guī)律。鑒于多線程編程已成為開發(fā)并發(fā)應(yīng)用程序的主流方法,學(xué)好本章的意義也便不言自明。