Linux系統(tǒng)編程(三) ------ 多線程編程

一、線程的創(chuàng)建和調(diào)度

1.線程是程序執(zhí)行的某一條指令流的映像。

為了進(jìn)一步減少處理機(jī)制的空轉(zhuǎn)時(shí)間,支持多處理器及減少上下文切換開銷,進(jìn)程在演化中出現(xiàn)了另一個(gè)概念——線程。它是進(jìn)程內(nèi)獨(dú)立的一條運(yùn)行路線,是處理器調(diào)用的最小單元,也可以成為輕量級(jí)進(jìn)程。
進(jìn)程標(biāo)識(shí)符在內(nèi)部唯一,只為了進(jìn)程內(nèi)的區(qū)分。
線程可以對(duì)進(jìn)程的內(nèi)存空間和資源進(jìn)程訪問,并與同一個(gè)進(jìn)程中的其他線程共享。因此,雖然每個(gè)線程同樣得開辟一定大小空間擁有自己的??臻g,擁有獨(dú)立的執(zhí)行序列,但線程上下文切換的開銷比創(chuàng)建進(jìn)程小得多。
在串行程序基礎(chǔ)上引入線程和進(jìn)程是為了提高程序的并發(fā)度,從而提高程序運(yùn)行效率和響應(yīng)時(shí)間。

線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):

線程執(zhí)行開銷小,但不利于資源的管理和保護(hù);而進(jìn)程正相反。同時(shí),線程適合于在SMP機(jī)器上運(yùn)行,而進(jìn)程則可以跨機(jī)器遷移。

2.線程函數(shù),用于提供線程執(zhí)行的指令(代碼)。

向線程函數(shù)傳遞參數(shù)分為兩種:
1)線程函數(shù)只有一個(gè)參數(shù)的情況:直接定義一個(gè)變量通過應(yīng)用傳給線程函數(shù)。
2)線程函數(shù)有多個(gè)參數(shù)的情況:此時(shí)就必須申明一個(gè)結(jié)構(gòu)體來包含所有的參數(shù),然后在傳入線程函數(shù)。

3.線程庫

#include <pthread.h>```
Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread。
pthread_t 在頭文件/usr/include/bits/pthreadtypes.h中定義:

    typedef unsigned long int pthread_t;
它是一個(gè)線程的標(biāo)識(shí)符。在編譯命令末注意加上-lpthread參數(shù),以調(diào)用靜態(tài)鏈接庫。因?yàn)閜thread并非Linux系統(tǒng)的默認(rèn)庫。

####4.使用pthread_create()函數(shù)創(chuàng)建線程:
>函數(shù)原型
```c
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void *(*start_routine)(void *), void *arg);```

*函數(shù)參數(shù)及說明:*
- 第一個(gè)參數(shù)為指向線程標(biāo)識(shí)符的指針。返回成功時(shí),由tid指向的內(nèi)存單元被設(shè)置為新創(chuàng)建線程的線程ID。
- 第二個(gè)參數(shù)用來設(shè)置各種不同的線程屬性。
- 第三個(gè)參數(shù)是線程運(yùn)行函數(shù)的起始地址。新創(chuàng)建的線程從start_routine函數(shù)的地址開始運(yùn)行,該函數(shù)只有一個(gè)萬能指針參數(shù)arg,如果需要向start_routine函數(shù)傳遞的參數(shù)不止一個(gè),那么需要把這些參數(shù)放到一個(gè)結(jié)構(gòu)中,然后把這個(gè)結(jié)構(gòu)的地址作為arg的參數(shù)傳入。
- 最后一個(gè)參數(shù)是運(yùn)行函數(shù)的參數(shù)。arg指針是最初唯一對(duì)指針?biāo)赶虻膶?duì)象進(jìn)行存取的方法,僅當(dāng)?shù)诙€(gè)指針基于第一個(gè)時(shí),才能對(duì)對(duì)象進(jìn)行存取。對(duì)對(duì)象的存取都限定于基于由arg指針表達(dá)式中。 由arg指針主要用于函數(shù)形參,或指向由 malloc() 分配的內(nèi)存空間。arg 數(shù)據(jù)類型不改變程序的語義。編譯器能通過作出arg指針是存取對(duì)象的唯一方法的假設(shè),更好地優(yōu)化某些類型的進(jìn)程。

*函數(shù)返回值:*函數(shù)成功返回0。任何其他返回值都表示錯(cuò)誤。

####5.使用pthread_join()函數(shù)以阻塞的方式等待thread指定的線程結(jié)束。
```c
 int pthread_join(pthread_t thread, void **retval);

函數(shù)參數(shù)及說明:

  • 第一個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,標(biāo)識(shí)唯一線程。
  • 第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來存儲(chǔ)被等待線程的返回值。

這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源被收回。如果線程已經(jīng)結(jié)束,那么該函數(shù)會(huì)立即返回。
如果沒有pthread_join()主線程會(huì)很快結(jié)束從而使整個(gè)進(jìn)程結(jié)束,從而使創(chuàng)建的線程沒有機(jī)會(huì)開始執(zhí)行就結(jié)束了。加入pthread_join()后,主線程會(huì)一直等待直到等待的線程結(jié)束自己才結(jié)束,使創(chuàng)建的線程有機(jī)會(huì)執(zhí)行。

函數(shù)返回值:函數(shù)執(zhí)行成功返回0。任何其他返回值都表示錯(cuò)誤。

提示:

在多線程編程的時(shí)候我們往往都是以for循環(huán)的形式調(diào)用,實(shí)際上主線程在第1個(gè)線程處就掛起了,在等待1號(hào)線程結(jié)束后再等待2號(hào)線程。當(dāng)然會(huì)出現(xiàn)3,4,5比1,2先結(jié)束的情況。主線程還是在等待1,2結(jié)束后,發(fā)現(xiàn)3,4,5其實(shí)早已經(jīng)結(jié)束了,就會(huì)回收3,4,5的資源,然后主線程再退出。

6.使用pthread_exit()函數(shù)終止并線程

void pthread_exit(void *retval);```
*函數(shù)說明及返回值:*
是線程的主動(dòng)行為;由于一個(gè)進(jìn)程中的多個(gè)線程是共享數(shù)據(jù)段的,因此通常在線程退出之后,退出線程所占用的資源并不會(huì)隨著線程的終止而得到釋放,但是可以用pthread_join()函數(shù)來配合同步并釋放資源。pthread_exit()終止調(diào)用它的線程并返回一個(gè)指向某個(gè)對(duì)象的指針,該返回值可以通過pthread_join函數(shù)的第二個(gè)參數(shù)得到。
唯一的參數(shù)是函數(shù)的返回代碼,只要pthread_join中的第二個(gè)參數(shù)不是NULL,這個(gè)值將被傳遞給retval。要說明的是,一個(gè)線程不能被多個(gè)線程等待,否則第一個(gè)接收到信號(hào)的線程成功返回,其余調(diào)用pthread_join的線程則返回錯(cuò)誤代碼。

####7.使用pthread_self()函數(shù)獲取當(dāng)前線程的標(biāo)識(shí)符:
```c
pthread_t pthread_self(void);```
返回獲得當(dāng)前線程自身的標(biāo)示符ID。pthread_t的類型為unsigned long int,所以在打印的時(shí)候要使用%ld方式,否則將產(chǎn)生奇怪的結(jié)果。

####8.使用pthread_equal()函數(shù)比較判斷是否是同一個(gè)線程:
```c
int pthread_equal(pthread_t tid1, pthread_t tid2);```
如果tid1和tid2相同,函數(shù)返回一個(gè)非0值,否則返回0。
如果tid1或tid2中任何一個(gè)是非法值,則返回將是不可預(yù)料的。

####9.使用pthread_cancel()取消指定線程:
```c
int pthread_cancel(pthread_t thread);```
*函數(shù)返回值:*函數(shù)成功返回0。任何其他返回值都表示錯(cuò)誤。
退出一個(gè)線程。
如何響應(yīng)退出請(qǐng)求取決于目標(biāo)線程的狀態(tài)。
當(dāng)然,線程也不是被動(dòng)的被別人結(jié)束。它可以通過設(shè)置自身的屬性來決定如何結(jié)束。
線程的被動(dòng)結(jié)束分為兩種,一種是異步終結(jié),另外一種是同步終結(jié)。異步終結(jié)就是當(dāng)其他線程調(diào)用 pthread_cancel的時(shí)候,線程就立刻被結(jié)束。而同步終結(jié)則不會(huì)立刻終結(jié),它會(huì)繼續(xù)運(yùn)行,直到到達(dá)下一個(gè)結(jié)束點(diǎn)(cancellation point)。當(dāng)一個(gè)線程被按照默認(rèn)的創(chuàng)建方式創(chuàng)建,那么它的屬性是同步終結(jié)。
發(fā)送終止信號(hào)給thread線程,如果成功則返回0,否則為非0值。發(fā)送成功并不意味著thread會(huì)終止。

##二、線程并發(fā)要求 
1.同步,代碼依賴資源有前后關(guān)系(競(jìng)爭(zhēng)),試衣間(互斥)。進(jìn)程/線程中的部分指令需要按照一定 順序前后執(zhí)行。
2.競(jìng)爭(zhēng),有競(jìng)爭(zhēng)才會(huì)同步。對(duì)于有限資源的共享使用過程中產(chǎn)生的競(jìng)爭(zhēng)關(guān)系。
3.互斥,對(duì)于共享資源的操作同時(shí)只能有一個(gè)進(jìn)程/線程。
4.死鎖,互相等待資源。//不應(yīng)有
5.饑餓,長時(shí)間(設(shè)置時(shí)間點(diǎn))無法獲取資源。//不應(yīng)有
6.異步,完全沒有關(guān)系。不用去保護(hù)。進(jìn)程/線程之間的指令執(zhí)行無順序。
[并發(fā)性:互斥和同步、死鎖和饑餓 ](http://blog.csdn.net/u013271921/article/details/45459351)
[進(jìn)程同步互斥——不死鎖的哲學(xué)家問題](http://www.oschina.net/code/snippet_180916_7504)
[操作系統(tǒng)中的互斥,同步與死鎖 ](http://blog.csdn.net/t_tbread/article/details/22678549)

##三、線程間通信
信息數(shù)據(jù)交換,使用多個(gè)線程都可見的內(nèi)存區(qū)域。A產(chǎn)生的B使用。標(biāo)識(shí)符

###保護(hù)機(jī)制:
#####1.線程互斥鎖: 
**互斥量(Mutex)**是“mutual exclusion”的縮寫?;コ饬渴菍?shí)現(xiàn)線程同步,和保護(hù)同時(shí)寫共享數(shù)據(jù)的主要方法。保障有同一把鎖保護(hù)的共享資源被多個(gè)線程互斥訪問。互斥量對(duì)共享數(shù)據(jù)的保護(hù)就像一把鎖。
######互斥量必須用類型pthread_mutex_t類型聲明。
對(duì)臨界區(qū)加鎖以實(shí)現(xiàn)互斥,當(dāng)某個(gè)進(jìn)程進(jìn)入臨界區(qū)之后,它將鎖上臨界區(qū),直到它退出臨界區(qū)為止。
>互斥鎖初始化函數(shù)
```c
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);```

***函數(shù)說明:***
    該函數(shù)用于C函數(shù)的多線程編程中,互斥鎖的初始化。
***函數(shù)參數(shù):***
函數(shù)是以動(dòng)態(tài)方式創(chuàng)建互斥鎖的,第一個(gè)參數(shù) mutex 是指向要初始化的互斥鎖的指針。第二個(gè)參數(shù) attr 是指向新建互斥鎖屬性對(duì)象的指針,該屬性對(duì)象定義要初始化的互斥鎖的屬性。如果該指針為 NULL,則使用默認(rèn)的快速互斥鎖屬性。
互斥鎖的屬性在創(chuàng)建鎖的時(shí)候指定,在LinuxThreads實(shí)現(xiàn)中僅有一個(gè)鎖類型屬性,不同的鎖類型在試圖對(duì)一個(gè)已經(jīng)被鎖定的互斥鎖加鎖時(shí)表現(xiàn)不同。
***函數(shù)返回值:***執(zhí)行成功完成之后會(huì)返回0,其他任何返回值都表示出現(xiàn)了錯(cuò)誤。
函數(shù)成功執(zhí)行后,互斥鎖被初始化為鎖住態(tài)。
[線程同步:互斥量,死鎖](http://blog.csdn.net/tototuzuoquan/article/details/39553761)

>互斥鎖的獲?。渔i)
```c
 int pthread_mutex_lock(pthread_mutex_t *mutex);```
互斥鎖的釋放(解鎖)
```c
 int pthread_mutex_unlock(pthread_mutex_t *mutex);```
以上兩個(gè)不可能被兩個(gè)不同的線程同時(shí)得到,而必須等待解鎖。
在同一進(jìn)程中的線程,如果加鎖后沒有解鎖,則任何其他線程都無法再獲得鎖。
pthread_mutex_lock()聲明開始用互斥鎖上鎖,此后的代碼直至調(diào)用pthread_mutex_unlock()為止,均被上鎖,即同一時(shí)間只能被一個(gè)線程調(diào)用執(zhí)行。當(dāng)一個(gè)線程執(zhí)行到pthread_mutex_lock()處時(shí),如果該鎖此時(shí)被另一個(gè)線程使用,那此線程被阻塞,即程序?qū)⒌却搅硪粋€(gè)線程釋放此互斥鎖。

多個(gè)線程對(duì)資源改寫都要互斥(保護(hù))
***提示:***總體來講, 有幾個(gè)不成文的基本原則:
- 對(duì)共享資源操作前一定要獲得鎖。
- 完成操作以后一定要釋放鎖。
- 盡量短時(shí)間地占用鎖。
- 如果有多鎖, 如獲得順序是ABC連環(huán)扣, 釋放順序也應(yīng)該是ABC。
- 線程錯(cuò)誤返回時(shí)應(yīng)該釋放它所獲得的鎖。

[pthreads線程(二) 線程同步--互斥量/鎖](http://www.cnblogs.com/dongsheng/p/4186358.html)

####2.線程信號(hào)量:解決多個(gè)線程在使用共享有限資源的同步問題。
線程的信號(hào)量與進(jìn)程間通信中使用的信號(hào)量的概念是一樣,它是一種特殊的變量,它可以被增加或減少,但對(duì)其的關(guān)鍵訪問被保證是原子操作。如果一個(gè)程序中有多個(gè)線程試圖改變一個(gè)信號(hào)量的值,系統(tǒng)將保證所有的操作都將依次進(jìn)行。
而只有0和1兩種取值的信號(hào)量叫做二進(jìn)制信號(hào)量,在這里將重點(diǎn)介紹。而信號(hào)量一般常用于保護(hù)一段代碼,使其每次只被一個(gè)執(zhí)行線程運(yùn)行。我們可以使用二進(jìn)制信號(hào)量來完成這個(gè)工作。

信號(hào)量是一個(gè)計(jì)數(shù)器,用于控制訪問有限共享資源的線程數(shù)。
在操作系統(tǒng)中,信號(hào)量是一整數(shù),在sem大于等于零時(shí)代表可供并發(fā)進(jìn)程使用的資源實(shí)體數(shù),但sem小于零時(shí)表示正在等待使用臨界區(qū)的進(jìn)程數(shù);
線程信號(hào)量,sem_t


>信號(hào)量的創(chuàng)建和初始化:
```c
#include <semaphore.h>
int sem_init (sem_t *sem, int pshared, unsigned int value);```
該函數(shù)初始化由sem指向的信號(hào)對(duì)象,設(shè)置它的共享選項(xiàng),并給它一個(gè)初始的整數(shù)值。pshared控制信號(hào)量的類型,如果其值為0,就表示這個(gè)信號(hào)量是當(dāng)前進(jìn)程的局部信號(hào)量,否則信號(hào)量就可以在多個(gè)進(jìn)程之間共享,value為sem的初始值。調(diào)用成功時(shí)返回0,失敗返回-1。

>信號(hào)量的獲取,以原子操作的方式將信號(hào)量的值--1。
```c
int sem_wait(sem_t * sem);```
sem指向的對(duì)象是由sem_init調(diào)用初始化的信號(hào)量。調(diào)用成功時(shí)返回0,失敗返回-1。

>信號(hào)量的釋放,以原子操作的方式將信號(hào)量的值++1。
```c
int sem_post(sem_t * sem);```
與sem_wait一樣,sem指向的對(duì)象是由sem_init調(diào)用初始化的信號(hào)量。調(diào)用成功時(shí)返回0,失敗返回-1。

>信號(hào)量的銷毀,用于對(duì)用完的信號(hào)量的清理。
```c
int sem_destroy (sem_t *sem);```
成功時(shí)返回0,失敗時(shí)返回-1。

[Linux多線程——使用信號(hào)量同步線程 ](http://blog.csdn.net/ljianhui/article/details/10813469/)
[線程同步(互斥鎖與信號(hào)量的作用與區(qū)別)](http://blog.csdn.net/tietao/article/details/7367827)

####3.原子操作
暨如果兩個(gè)線程企圖同時(shí)給一個(gè)信號(hào)量++1或--1,它們之間不會(huì)互相干擾。是最安全的操作,用鎖鎖在一起,或全執(zhí)行,或全擱置。


#####課程使用各參數(shù)(暫時(shí))安排

    創(chuàng)建線程:
    #include <pthread.h>
    //全局變量,可以是結(jié)構(gòu)體,整型變量,等等。
    void *thread_func(void *arg);//聲明線程函數(shù)
    //主調(diào)函數(shù)內(nèi):
    pthread_t thread_id;    //注冊(cè)定義線程id
    pthread_create(&thread_id, NULL, thread_func, NULL); //創(chuàng)建線程并指定線程的執(zhí)行函數(shù),最后一個(gè)參數(shù)為傳到線程函數(shù)的參數(shù)。
     pthread_join(thread_id, NULL); // 等待指定線程的退出
     //自定義線程函數(shù)內(nèi):
     pthread_exit(NULL);//返回到j(luò)oin后一個(gè)參數(shù)。


    線程互斥鎖:
            pthread_mutex_t mutex;//全局定義線程互斥鎖
            主調(diào)函數(shù)內(nèi):  
            注冊(cè)定義線程id
            pthread_mutex_init(&mutex, NULL);//初始化線程互斥鎖
            創(chuàng)建線程
            等待線程退出
            自定義線程函數(shù)內(nèi):
            pthread_mutex_lock(&mutex);// 獲取互斥鎖
            pthread_mutex_unlock(&mutex); // 釋放互斥鎖
            ??!互斥鎖的獲取和釋放必須成對(duì)出現(xiàn)!!
    
    信號(hào)量:
            sem_t sem;//全局定義信號(hào)量
            void sig_handler(int signo);//聲明信號(hào)處理函數(shù)
            主調(diào)函數(shù)內(nèi):  
            注冊(cè)定義線程id
            signal(SIGINT, sig_handler);//注冊(cè)信號(hào)處理函數(shù)
            sem_init(&sem, 0, 0);// 初始化信號(hào)量為0
            創(chuàng)建線程
            等待線程退出
            sem_destroy(&sem);// 銷毀信號(hào)量
            自定義線程函數(shù)內(nèi):
            while(1)
            sem_wait(&sem);// sem -1 對(duì)信號(hào)量進(jìn)行wait(即-1)操作,如果<0,阻塞。
            自定義信號(hào)處理函數(shù)內(nèi):
            sem_post(&sem);//sem +1 對(duì)信號(hào)量進(jìn)行post(即+1)操作,如果<=0,系統(tǒng)喚醒一個(gè)等待線程。
[進(jìn)程同步互斥——不死鎖的哲學(xué)家問題](http://www.oschina.net/code/snippet_180916_7504)
[競(jìng)爭(zhēng)與同步,互斥量,信號(hào)量,死鎖,條件變量,哲學(xué)家吃飯問題](http://www.th7.cn/system/lin/201509/134476.shtml)
####參考資料
[對(duì)Linux中多線程編程中pthread_join的理解](http://www.linuxidc.com/Linux/2013-09/89931.htm)
[Linux 線程操作函數(shù)技能總結(jié)](http://blog.csdn.net/shaderdx/article/details/50475982)
[pthread多線程編程的學(xué)習(xí)小結(jié)](https://www.oschina.net/question/234345_40365)
[pThreads線程(一) 基本API](http://www.cnblogs.com/dongsheng/p/4184153.html)
[Linux Pthread 深入解析](http://blog.csdn.net/u010009623/article/details/53116814)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay閱讀 1,709評(píng)論 0 52
  • 簡介 線程創(chuàng)建 線程屬性設(shè)置 線程參數(shù)傳遞 線程優(yōu)先級(jí) 線程的數(shù)據(jù)處理 線程的分離狀態(tài) 互斥鎖 信號(hào)量 一 線程創(chuàng)...
    第八區(qū)閱讀 8,692評(píng)論 1 6
  • 系統(tǒng)與網(wǎng)絡(luò)編程 小作業(yè) 公交車停發(fā)車程序 線程 并發(fā)執(zhí)行:看起來像同時(shí)運(yùn)行,實(shí)際上在單核cpu里只有一個(gè)。將其排成...
    I踏雪尋梅閱讀 504評(píng)論 0 3
  • 線程基礎(chǔ) 線程是進(jìn)程的一個(gè)執(zhí)行單元,執(zhí)行一段程序片段,線程共享全局變量;線程的查看可以使用命令或者文件來進(jìn)行查看;...
    秋風(fēng)弄影閱讀 801評(píng)論 0 0
  • 摘要 線程概念,線程與進(jìn)程的區(qū)別與聯(lián)系學(xué)會(huì)線程控制,線程創(chuàng)建,線程終止,線程等待了解線程分離與線程安全學(xué)會(huì)線程同步...
    狼之足跡閱讀 520評(píng)論 2 3

友情鏈接更多精彩內(nèi)容