1.Linux“線程” 進程與線程之間是有區(qū)別的,不過Linux內(nèi)核只提供了輕量進程的支持,未實現(xiàn)線程模型。Linux是一種“多進程單線程”的操作系統(tǒng)。Linux本身只有進程的概念,而其所謂的“線程”本質(zhì)上在內(nèi)核里仍然是進程。
大家知道,進程是資源分配的單位,同一進程中的多個線程共享該進程的資源(如作為共享內(nèi)存的全局變量)。Linux中所謂的“線程”只是在被創(chuàng)建時
clone了父進程的資源,因此clone出來的進程表現(xiàn)為“線程”,這一點一定要弄清楚。因此,Linux“線程”這個概念只有在打冒號的情況下才是最
準確的。
目前Linux中最流行的線程機制為LinuxThreads,所采用的就是線程-進程“一對一”模型,調(diào)度交給核心,而在用戶級實現(xiàn)一個包括信號處理在
內(nèi)的線程管理機制。LinuxThreads由Xavier Leroy
(Xavier.Leroy@inria.fr)負責開發(fā)完成,并已綁定在GLIBC中發(fā)行,它實現(xiàn)了一種BiCapitalized面向Linux的
Posix 1003.1c “pthread”標準接口。Linuxthread可以支持Intel、Alpha、MIPS等平臺上的多處理器系統(tǒng)。
按照POSIX 1003.1c 標準編寫的程序與Linuxthread 庫相鏈接即可支持Linux平臺上的多線程,在程序中需包含頭文件pthread. h,在編譯鏈接時使用命令:
gcc -D -REENTRANT -lpthread xxx. c |
其中-REENTRANT宏使得相關庫函數(shù)(如stdio.h、errno.h中函數(shù))
是可重入的、線程安全的(thread-safe),-lpthread則意味著鏈接庫目錄下的libpthread.a或libpthread.so文
件。使用Linuxthread庫需要2.0以上版本的Linux內(nèi)核及相應版本的C庫(libc 5.2.18、libc 5.4.12、libc
6)。 2.“線程”控制 線程創(chuàng)建 進程被創(chuàng)建時,系統(tǒng)會為其創(chuàng)建一個主線程,而要在進程中創(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ù)。 每個線程都有自己的線程ID,以便在進程內(nèi)區(qū)分。線程ID在pthread_create調(diào)用時回返給創(chuàng)建線程的調(diào)用者;一個線程也可以在創(chuàng)建后使用pthread_self()調(diào)用獲取自己的線程ID:
線程退出 線程的退出方式有三: (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 指定的線程。 如果一個線程要等待另一個線程的終止,可以使用pthread_join函數(shù),該函數(shù)的作用是調(diào)用pthread_join的線程將被掛起直到線程ID為參數(shù)thread的線程終止:
pthread_join (pthread_t thread, void** threadreturn); | 3.線程通信 線程互斥
互斥意味著“排它”,即兩個線程不能同時進入被互斥保護的代碼。Linux下可以通過pthread_mutex_t
定義互斥體機制完成多線程的互斥操作,該機制的作用是對某個需要互斥的部分,在進入時先得到互斥體,如果沒有得到互斥體,表明互斥部分被其它線程擁有,此
時欲獲取互斥體的線程阻塞,直到擁有該互斥體的線程完成互斥部分的操作為止。 下面的代碼實現(xiàn)了對共享全局變量x 用互斥體mutex 進行保護的目的:
int x; // 進程中的全局變量 pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); //按缺省的屬性初始化互斥體變量mutex pthread_mutex_lock(&mutex); // 給互斥體變量加鎖 … //對變量x 的操作 phtread_mutex_unlock(&mutex); // 給互斥體變量解除鎖 | 線程同步 同步就是線程等待某個事件的發(fā)生。只有當?shù)却氖录l(fā)生線程才繼續(xù)執(zhí)行,否則線程掛起并放棄處理器。當多個線程協(xié)作時,相互作用的任務必須在一定的條件下同步。 Linux下的C語言編程有多種線程同步機制,最典型的是條件變量(condition variable)。pthread_cond_init用來創(chuàng)建一個條件變量,其函數(shù)原型為:
pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr); |
pthread_cond_wait和pthread_cond_timedwait用來等待條件變量被設置,值得注意的是這兩個等待調(diào)用需要一個已經(jīng)上
鎖的互斥體mutex,這是為了防止在真正進入等待狀態(tài)之前別的線程有可能設置該條件變量而產(chǎn)生競爭。pthread_cond_wait的函數(shù)原型為:
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex); | pthread_cond_broadcast用于設置條件變量,即使得事件發(fā)生,這樣等待該事件的線程將不再阻塞:
pthread_cond_broadcast (pthread_cond_t *cond) ; | pthread_cond_signal則用于解除某一個等待線程的阻塞狀態(tài):
pthread_cond_signal (pthread_cond_t *cond) ; | pthread_cond_destroy 則用于釋放一個條件變量的資源。 在頭文件semaphore.h 中定義的信號量則完成了互斥體和條件變量的封裝,按照多線程程序設計中訪問控制機制,控制對資源的同步訪問,提供程序設計人員更方便的調(diào)用接口。
sem_init(sem_t *sem, int pshared, unsigned int val); | 這個函數(shù)初始化一個信號量sem 的值為val,參數(shù)pshared 是共享屬性控制,表明是否在進程間共享。
調(diào)用該函數(shù)時,若sem為無狀態(tài),調(diào)用線程阻塞,等待信號量sem值增加(post )成為有信號狀態(tài);若sem為有狀態(tài),調(diào)用線程順序執(zhí)行,但信號量的值減一。
調(diào)用該函數(shù),信號量sem的值增加,可以從無信號狀態(tài)變?yōu)橛行盘枲顟B(tài)。 4.實例
下面我們還是以名的生產(chǎn)者/消費者問題為例來闡述Linux線程的控制和通信。一組生產(chǎn)者線程與一組消費者線程通過緩沖區(qū)發(fā)生聯(lián)系。生產(chǎn)者線程將生產(chǎn)的產(chǎn)品送入緩沖區(qū),消費者線程則從中取出產(chǎn)品。緩沖區(qū)有N 個,是一個環(huán)形的緩沖池。  #include <stdio.h> #include <pthread.h> #define BUFFER_SIZE 16 // 緩沖區(qū)數(shù)量 struct prodcons { // 緩沖區(qū)相關數(shù)據(jù)結構 int buffer[BUFFER_SIZE]; /* 實際數(shù)據(jù)存放的數(shù)組*/ pthread_mutex_t lock; /* 互斥體lock 用于對緩沖區(qū)的互斥操作 */ int readpos, writepos; /* 讀寫指針*/ pthread_cond_t notempty; /* 緩沖區(qū)非空的條件變量 */ pthread_cond_t notfull; /* 緩沖區(qū)未滿的條件變量 */ }; /* 初始化緩沖區(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; } /* 將產(chǎn)品放入緩沖區(qū),這里是存入一個整數(shù)*/ void put(struct prodcons *b, int data) { pthread_mutex_lock(&b->lock); /* 等待緩沖區(qū)未滿*/ if ((b->writepos + 1) % BUFFER_SIZE == b->readpos) { pthread_cond_wait(&b->notfull, &b->lock); } /* 寫數(shù)據(jù),并移動指針 */ b->buffer[b->writepos] = data; b->writepos++; if (b->writepos >= BUFFER_SIZE) b->writepos = 0; /* 設置緩沖區(qū)非空的條件變量*/ pthread_cond_signal(&b->notempty); pthread_mutex_unlock(&b->lock); } /* 從緩沖區(qū)中取出整數(shù)*/ int get(struct prodcons *b) { int data; pthread_mutex_lock(&b->lock); /* 等待緩沖區(qū)非空*/ if (b->writepos == b->readpos) { pthread_cond_wait(&b->notempty, &b->lock); } /* 讀數(shù)據(jù),移動讀指針*/ data = b->buffer[b->readpos]; b->readpos++; if (b->readpos >= BUFFER_SIZE) b->readpos = 0; /* 設置緩沖區(qū)未滿的條件變量*/ pthread_cond_signal(&b->notfull); pthread_mutex_unlock(&b->lock); return data; }
/* 測試:生產(chǎn)者線程將1 到10000 的整數(shù)送入緩沖區(qū),消費者線 程從緩沖區(qū)中獲取整數(shù),兩者都打印信息*/ #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); /* 創(chuàng)建生產(chǎn)者和消費者線程*/ 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的多任務并發(fā)程序設計》(《軟件報》2006年5~12期連載)、《深入淺出Win32多線程程序設計》(天極網(wǎng)技術專題)系列,我們來找出這兩個系列文章與本文的共通點。
看待技術問題要瞄準其本質(zhì),不管是Linux、VxWorks還是WIN32,其涉及到多線程的部分都是那些內(nèi)容,無非就是線程控制和線程通信,它們的許多函數(shù)只是名稱不同,其實質(zhì)含義是等價的,下面我們來列個三大操作系統(tǒ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)建信號量 |
CreateSemaphore |
semBCreate、semCCreate |
sem_init |
等待信號量 |
WaitForSingleObject |
semTake |
sem_wait |
釋放信號量 |
ReleaseSemaphore |
semGive |
sem_post |
6.小結
本章講述了Linux下多線程的控制及線程間通信編程方法,給出了一個生產(chǎn)者/消費者的實例,并將Linux的多線程與WIN32、VxWorks多線程進行了類比,總結了一般規(guī)律。鑒于多線程編程已成為開發(fā)并發(fā)應用程序的主流方法,學好本章的意義也便不言自明。
完
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <stdio.h>
#include <stdio.h>
#include <pthread.h>
void thread ( void )
{
int i;
for (i=0;i<3;i++)
printf ( "This is a pthread.\n" );
}
int main( void )
{
pthread_t id;
int i,ret;
ret=pthread_create(&id,NULL,( void *) thread ,NULL);
if (ret!=0){
printf ( "Create pthread error!\n" );
exit (1);
}
for (i=0;i<3;i++)
printf ( "This is the main process.\n" );
pthread_join(id,NULL);
return (0);
}
|
編譯:
gcc example1.c -lpthread -o example1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #include <pthread.h>
#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#define MAX 10
pthread_t thread [2];
pthread_mutex_t mut;
int number=0, i;
void *thread1()
{
printf ( "thread1 : I'm thread 1\n" );
for (i = 0; i < MAX; i++)
{
printf ( "thread1 : number = %d\n" ,number);
pthread_mutex_lock(&mut);
number++;
pthread_mutex_unlock(&mut);
sleep(2);
}
printf ( "thread1 :主函數(shù)在等我完成任務嗎?\n" );
pthread_exit(NULL);
}
void *thread2()
{
printf ( "thread2 : I'm thread 2\n" );
for (i = 0; i < MAX; i++)
{
printf ( "thread2 : number = %d\n" ,number);
pthread_mutex_lock(&mut);
number++;
pthread_mutex_unlock(&mut);
sleep(3);
}
printf ( "thread2 :主函數(shù)在等我完成任務嗎?\n" );
pthread_exit(NULL);
}
void thread_create( void )
{
int temp;
memset (& thread , 0, sizeof ( thread ));
if ((temp = pthread_create(& thread [0], NULL, thread1, NULL)) != 0)
printf ( "線程1創(chuàng)建失敗!\n" );
else
printf ( "線程1被創(chuàng)建\n" );
if ((temp = pthread_create(& thread [1], NULL, thread2, NULL)) != 0)
printf ( "線程2創(chuàng)建失敗" );
else
printf ( "線程2被創(chuàng)建\n" );
}
void thread_wait( void )
{
if ( thread [0] !=0) {
pthread_join( thread [0],NULL);
printf ( "線程1已經(jīng)結束\n" );
}
if ( thread [1] !=0) {
pthread_join( thread [1],NULL);
printf ( "線程2已經(jīng)結束\n" );
}
}
int main()
{
pthread_mutex_init(&mut,NULL);
printf ( "我是主函數(shù)哦,我正在創(chuàng)建線程,呵呵\n" );
thread_create();
printf ( "我是主函數(shù)哦,我正在等待線程完成任務阿,呵呵\n" );
thread_wait();
return 0;
}
|
編譯 :
gcc -lpthread -o thread_example lp.c
|