linux多線程編程(轉)

原文地址:http://www.cnblogs.com/BiffoLee/archive/2011/11/18/2254540.htmlhtml

1.Linux「線程」web

     進程與線程之間是有區別的,不過Linux內核只提供了輕量進程的支持,未實現線程模型。Linux是一種「多進程單線程」的操做系統。Linux自己只有進程的概念,而其所謂的「線程」本質上在內核裏仍然是進程。編程

     你們知道,進程是資源分配的單位,同一進程中的多個線程共享該進程的資源(如做爲共享內存的全局變量)。Linux中所謂的「線程」只是在被建立時clone了父進程的資源,所以clone出來的進程表現爲「線程」,這一點必定要弄清楚。所以,Linux「線程」這個概念只有在打冒號的狀況下才是最準確的。api

     目前Linux中最流行的線程機制爲LinuxThreads,所採用的就是線程-進程「一對一」模型,調度交給核心,而在用戶級實現一個包括信號處理在內的線程管理機制。LinuxThreads由Xavier Leroy (Xavier.Leroy@inria.fr)負責開發完成,並已綁定在GLIBC中發行,它實現了一種BiCapitalized面向Linux的Posix 1003.1c 「pthread」標準接口。Linuxthread能夠支持Intel、Alpha、MIPS等平臺上的多處理器系統。    

按照POSIX 1003.1c 標準編寫的程序與Linuxthread 庫相連接便可支持Linux平臺上的多線程,在程序中需包含頭文件pthread. h,在編譯連接時使用命令:     

數組

gcc -D -REENTRANT -lpthread xxx. c


其中-REENTRANT宏使得相關庫函數(如stdio.h、errno.h中函數) 是可重入的、線程安全的(thread-safe),-lpthread則意味着連接庫目錄下的libpthread.a或libpthread.so文件。使用Linuxthread庫須要2.0以上版本的Linux內核及相應版本的C庫(libc 5.2.1八、libc 5.4.十二、libc 6)。安全

     2.「線程」控制     

線程建立     

進程被建立時,系統會爲其建立一個主線程,而要在進程中建立新的線程,則能夠調用pthread_create:     

數據結構

pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *          
(start_routine)(void*), void *arg);


start_routine爲新線程的入口函數,arg爲傳遞給start_routine的參數。     

每一個線程都有本身的線程ID,以便在進程內區分。線程ID在pthread_create調用時回返給建立線程的調用者;一個線程也能夠在建立後使用pthread_self()調用獲取本身的線程ID:     

多線程

pthread_self (void) ;


線程退出     

線程的退出方式有三:     

(1)執行完成後隱式退出;     

(2)由線程自己顯示調用pthread_exit 函數退出;     

併發

pthread_exit (void * retval) ;


(3)被其餘線程用pthread_cance函數終止:     

函數

pthread_cance (pthread_t thread) ;


在某線程中調用此函數,能夠終止由參數thread 指定的線程。     

若是一個線程要等待另外一個線程的終止,可使用pthread_join函數,該函數的做用是調用pthread_join的線程將被掛起直到線程ID爲參數thread的線程終止:     

pthread_join (pthread_t thread, void** threadreturn);

3.線程通訊      

線程互斥     

互斥意味着「排它」,即兩個線程不能同時進入被互斥保護的代碼。Linux下能夠經過pthread_mutex_t 定義互斥體機制完成多線程的互斥操做,該機制的做用是對某個須要互斥的部分,在進入時先獲得互斥體,若是沒有獲得互斥體,代表互斥部分被其它線程擁有,此時欲獲取互斥體的線程阻塞,直到擁有該互斥體的線程完成互斥部分的操做爲止。     

下面的代碼實現了對共享全局變量x 用互斥體mutex 進行保護的目的:     

int x; // 進程中的全局變量          
pthread_mutex_t mutex;           
pthread_mutex_init(&mutex, NULL); //按缺省的屬性初始化互斥體變量mutex           
pthread_mutex_lock(&mutex); // 給互斥體變量加鎖           
… //對變量x 的操做           
phtread_mutex_unlock(&mutex); // 給互斥體變量解除鎖


線程同步     

同步就是線程等待某個事件的發生。只有當等待的事件發生線程才繼續執行,不然線程掛起並放棄處理器。當多個線程協做時,相互做用的任務必須在必定的條件下同步。     

Linux下的C語言編程有多種線程同步機制,最典型的是條件變量(condition variable)。pthread_cond_init用來建立一個條件變量,其函數原型爲:     

pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);


pthread_cond_wait和pthread_cond_timedwait用來等待條件變量被設置,值得注意的是這兩個等待調用須要一個已經上鎖的互斥體mutex,這是爲了防止在真正進入等待狀態以前別的線程有可能設置該條件變量而產生競爭。pthread_cond_wait的函數原型爲:     

pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);


pthread_cond_broadcast用於設置條件變量,即便得事件發生,這樣等待該事件的線程將再也不阻塞:     

pthread_cond_broadcast (pthread_cond_t *cond) ;


pthread_cond_signal則用於解除某一個等待線程的阻塞狀態:     

pthread_cond_signal (pthread_cond_t *cond) ;


pthread_cond_destroy 則用於釋放一個條件變量的資源。     

在頭文件semaphore.h 中定義的信號量則完成了互斥體和條件變量的封裝,按照多線程程序設計中訪問控制機制,控制對資源的同步訪問,提供程序設計人員更方便的調用接口。     

sem_init(sem_t *sem, int pshared, unsigned int val);


這個函數初始化一個信號量sem 的值爲val,參數pshared 是共享屬性控制,代表是否在進程間共享。     

sem_wait(sem_t *sem);


調用該函數時,若sem爲無狀態,調用線程阻塞,等待信號量sem值增長(post )成爲有信號狀態;若sem爲有狀態,調用線程順序執行,但信號量的值減一。     

sem_post(sem_t *sem);


調用該函數,信號量sem的值增長,能夠從無信號狀態變爲有信號狀態。

    

 

4.實例      

下面咱們仍是以名的生產者/消費者問題爲例來闡述Linux線程的控制和通訊。一組生產者線程與一組消費者線程經過緩衝區發生聯繫。生產者線程將生產的產品送入緩衝區,消費者線程則從中取出產品。緩衝區有N 個,是一個環形的緩衝池。      
#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 16 // 緩衝區數量
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;
}

/* 測試:生產者線程將1 到10000 的整數送入緩衝區,消費者線
   程從緩衝區中獲取整數,二者都打印信息*/
#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.WIN3二、VxWorks、Linux線程類比    

目前爲止,筆者已經創做了《基於嵌入式操做系統VxWorks的多任務併發程序設計》(《軟件報》2006年5~12期連載)、《深刻淺出Win32多線程程序設計》(天極網技術專題)系列,咱們來找出這兩個系列文章與本文的共通點。    

看待技術問題要瞄準其本質,不論是Linux、VxWorks仍是WIN32,其涉及到多線程的部分都是那些內容,無非就是線程控制和線程通訊,它們的許多函數只是名稱不一樣,其實質含義是等價的,下面咱們來列個三大操做系統共同點詳細表單:    

事項 WIN32 VxWorks Linux
線程建立 CreateThread taskSpawn pthread_create
線程終止 執行完成後退出;線程自身調用ExitThread函數即終止本身;被其餘線程調用函數TerminateThread函數 執行完成後退出;由線程自己調用exit退出;被其餘線程調用函數taskDelete終止 執行完成後退出;由線程自己調用pthread_exit 退出;被其餘線程調用函數pthread_cance終止
獲取線程ID GetCurrentThreadId taskIdSelf pthread_self
建立互斥 CreateMutex semMCreate pthread_mutex_init
獲取互斥 WaitForSingleObject、WaitForMultipleObjects semTake pthread_mutex_lock
釋放互斥 ReleaseMutex semGive phtread_mutex_unlock
建立信號量 CreateSemaphore semBCreate、semCCreate sem_init
等待信號量 WaitForSingleObject semTake sem_wait
釋放信號量 ReleaseSemaphore semGive sem_post


6.小結    

本章講述了Linux下多線程的控制及線程間通訊編程方法,給出了一個生產者/消費者的實例,並將Linux的多線程與WIN3二、VxWorks多線程進行了類比,總結了通常規律。鑑於多線程編程已成爲開發併發應用程序的主流方法,學好本章的意義也便不言自明。

?
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 :主函數在等我完成任務嗎?\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 :主函數在等我完成任務嗎?\n" );
     pthread_exit(NULL);
}
 
 
void thread_create( void )
{
     int temp;
     memset (& thread , 0, sizeof ( thread )); //comment1
     //建立線程
     if ((temp = pthread_create(& thread [0], NULL, thread1, NULL)) != 0) //comment2
         printf ( "線程1建立失敗!\n" );
     else
         printf ( "線程1被建立\n" );
     if ((temp = pthread_create(& thread [1], NULL, thread2, NULL)) != 0) //comment3
         printf ( "線程2建立失敗" );
     else
         printf ( "線程2被建立\n" );
}
 
void thread_wait( void )
{
     //等待線程結束
     if ( thread [0] !=0) { //comment4
         pthread_join( thread [0],NULL);
         printf ( "線程1已經結束\n" );
     }
     if ( thread [1] !=0) { //comment5
         pthread_join( thread [1],NULL);
         printf ( "線程2已經結束\n" );
     }
}
 
int main()
{
     //用默認屬性初始化互斥鎖
     pthread_mutex_init(&mut,NULL);
     printf ( "我是主函數哦,我正在建立線程,呵呵\n" );
     thread_create();
     printf ( "我是主函數哦,我正在等待線程完成任務阿,呵呵\n" );
     thread_wait();
     return 0;
}

編譯 :

       gcc -lpthread -o thread_example lp.c

相關文章
相關標籤/搜索