linux多线程编写哲学家,Linux系统编程(三) ------ 多线程编程
一、線程的創建和調度
1.線程是程序執行的某一條指令流的映像。
為了進一步減少處理機制的空轉時間,支持多處理器及減少上下文切換開銷,進程在演化中出現了另一個概念——線程。它是進程內獨立的一條運行路線,是處理器調用的最小單元,也可以成為輕量級進程。
進程標識符在內部唯一,只為了進程內的區分。
線程可以對進程的內存空間和資源進程訪問,并與同一個進程中的其他線程共享。因此,雖然每個線程同樣得開辟一定大小空間擁有自己的棧空間,擁有獨立的執行序列,但線程上下文切換的開銷比創建進程小得多。
在串行程序基礎上引入線程和進程是為了提高程序的并發度,從而提高程序運行效率和響應時間。
線程和進程在使用上各有優缺點:
線程執行開銷小,但不利于資源的管理和保護;而進程正相反。同時,線程適合于在SMP機器上運行,而進程則可以跨機器遷移。
2.線程函數,用于提供線程執行的指令(代碼)。
向線程函數傳遞參數分為兩種:
1)線程函數只有一個參數的情況:直接定義一個變量通過應用傳給線程函數。
2)線程函數有多個參數的情況:此時就必須申明一個結構體來包含所有的參數,然后在傳入線程函數。
3.線程庫
#include ```
Linux系統下的多線程遵循POSIX線程接口,稱為pthread。
pthread_t 在頭文件/usr/include/bits/pthreadtypes.h中定義:
typedef unsigned long int pthread_t;
它是一個線程的標識符。在編譯命令末注意加上-lpthread參數,以調用靜態鏈接庫。因為pthread并非Linux系統的默認庫。
####4.使用pthread_create()函數創建線程:
>函數原型
```c
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void *(*start_routine)(void *), void *arg);```
*函數參數及說明:*
- 第一個參數為指向線程標識符的指針。返回成功時,由tid指向的內存單元被設置為新創建線程的線程ID。
- 第二個參數用來設置各種不同的線程屬性。
- 第三個參數是線程運行函數的起始地址。新創建的線程從start_routine函數的地址開始運行,該函數只有一個萬能指針參數arg,如果需要向start_routine函數傳遞的參數不止一個,那么需要把這些參數放到一個結構中,然后把這個結構的地址作為arg的參數傳入。
- 最后一個參數是運行函數的參數。arg指針是最初唯一對指針所指向的對象進行存取的方法,僅當第二個指針基于第一個時,才能對對象進行存取。對對象的存取都限定于基于由arg指針表達式中。 由arg指針主要用于函數形參,或指向由 malloc() 分配的內存空間。arg 數據類型不改變程序的語義。編譯器能通過作出arg指針是存取對象的唯一方法的假設,更好地優化某些類型的進程。
*函數返回值:*函數成功返回0。任何其他返回值都表示錯誤。
####5.使用pthread_join()函數以阻塞的方式等待thread指定的線程結束。
```c
int pthread_join(pthread_t thread, void **retval);
函數參數及說明:
第一個參數為被等待的線程標識符,標識唯一線程。
第二個參數為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。
這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。如果線程已經結束,那么該函數會立即返回。
如果沒有pthread_join()主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join()后,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行。
函數返回值:函數執行成功返回0。任何其他返回值都表示錯誤。
提示:
在多線程編程的時候我們往往都是以for循環的形式調用,實際上主線程在第1個線程處就掛起了,在等待1號線程結束后再等待2號線程。當然會出現3,4,5比1,2先結束的情況。主線程還是在等待1,2結束后,發現3,4,5其實早已經結束了,就會回收3,4,5的資源,然后主線程再退出。
6.使用pthread_exit()函數終止并線程
void pthread_exit(void *retval);```
*函數說明及返回值:*
是線程的主動行為;由于一個進程中的多個線程是共享數據段的,因此通常在線程退出之后,退出線程所占用的資源并不會隨著線程的終止而得到釋放,但是可以用pthread_join()函數來配合同步并釋放資源。pthread_exit()終止調用它的線程并返回一個指向某個對象的指針,該返回值可以通過pthread_join函數的第二個參數得到。
唯一的參數是函數的返回代碼,只要pthread_join中的第二個參數不是NULL,這個值將被傳遞給retval。要說明的是,一個線程不能被多個線程等待,否則第一個接收到信號的線程成功返回,其余調用pthread_join的線程則返回錯誤代碼。
####7.使用pthread_self()函數獲取當前線程的標識符:
```c
pthread_t pthread_self(void);```
返回獲得當前線程自身的標示符ID。pthread_t的類型為unsigned long int,所以在打印的時候要使用%ld方式,否則將產生奇怪的結果。
####8.使用pthread_equal()函數比較判斷是否是同一個線程:
```c
int pthread_equal(pthread_t tid1, pthread_t tid2);```
如果tid1和tid2相同,函數返回一個非0值,否則返回0。
如果tid1或tid2中任何一個是非法值,則返回將是不可預料的。
####9.使用pthread_cancel()取消指定線程:
```c
int pthread_cancel(pthread_t thread);```
*函數返回值:*函數成功返回0。任何其他返回值都表示錯誤。
退出一個線程。
如何響應退出請求取決于目標線程的狀態。
當然,線程也不是被動的被別人結束。它可以通過設置自身的屬性來決定如何結束。
線程的被動結束分為兩種,一種是異步終結,另外一種是同步終結。異步終結就是當其他線程調用 pthread_cancel的時候,線程就立刻被結束。而同步終結則不會立刻終結,它會繼續運行,直到到達下一個結束點(cancellation point)。當一個線程被按照默認的創建方式創建,那么它的屬性是同步終結。
發送終止信號給thread線程,如果成功則返回0,否則為非0值。發送成功并不意味著thread會終止。
##二、線程并發要求
1.同步,代碼依賴資源有前后關系(競爭),試衣間(互斥)。進程/線程中的部分指令需要按照一定 順序前后執行。
2.競爭,有競爭才會同步。對于有限資源的共享使用過程中產生的競爭關系。
3.互斥,對于共享資源的操作同時只能有一個進程/線程。
4.死鎖,互相等待資源。//不應有
5.饑餓,長時間(設置時間點)無法獲取資源。//不應有
6.異步,完全沒有關系。不用去保護。進程/線程之間的指令執行無順序。
[并發性:互斥和同步、死鎖和饑餓 ](http://blog.csdn.net/u013271921/article/details/45459351)
[進程同步互斥——不死鎖的哲學家問題](http://www.oschina.net/code/snippet_180916_7504)
[操作系統中的互斥,同步與死鎖 ](http://blog.csdn.net/t_tbread/article/details/22678549)
##三、線程間通信
信息數據交換,使用多個線程都可見的內存區域。A產生的B使用。標識符
###保護機制:
#####1.線程互斥鎖:
**互斥量(Mutex)**是“mutual exclusion”的縮寫。互斥量是實現線程同步,和保護同時寫共享數據的主要方法。保障有同一把鎖保護的共享資源被多個線程互斥訪問。互斥量對共享數據的保護就像一把鎖。
######互斥量必須用類型pthread_mutex_t類型聲明。
對臨界區加鎖以實現互斥,當某個進程進入臨界區之后,它將鎖上臨界區,直到它退出臨界區為止。
>互斥鎖初始化函數
```c
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);```
***函數說明:***
該函數用于C函數的多線程編程中,互斥鎖的初始化。
***函數參數:***
函數是以動態方式創建互斥鎖的,第一個參數 mutex 是指向要初始化的互斥鎖的指針。第二個參數 attr 是指向新建互斥鎖屬性對象的指針,該屬性對象定義要初始化的互斥鎖的屬性。如果該指針為 NULL,則使用默認的快速互斥鎖屬性。
互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性,不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。
***函數返回值:***執行成功完成之后會返回0,其他任何返回值都表示出現了錯誤。
函數成功執行后,互斥鎖被初始化為鎖住態。
[線程同步:互斥量,死鎖](http://blog.csdn.net/tototuzuoquan/article/details/39553761)
>互斥鎖的獲取(加鎖)
```c
int pthread_mutex_lock(pthread_mutex_t *mutex);```
互斥鎖的釋放(解鎖)
```c
int pthread_mutex_unlock(pthread_mutex_t *mutex);```
以上兩個不可能被兩個不同的線程同時得到,而必須等待解鎖。
在同一進程中的線程,如果加鎖后沒有解鎖,則任何其他線程都無法再獲得鎖。
pthread_mutex_lock()聲明開始用互斥鎖上鎖,此后的代碼直至調用pthread_mutex_unlock()為止,均被上鎖,即同一時間只能被一個線程調用執行。當一個線程執行到pthread_mutex_lock()處時,如果該鎖此時被另一個線程使用,那此線程被阻塞,即程序將等待到另一個線程釋放此互斥鎖。
多個線程對資源改寫都要互斥(保護)
***提示:***總體來講, 有幾個不成文的基本原則:
- 對共享資源操作前一定要獲得鎖。
- 完成操作以后一定要釋放鎖。
- 盡量短時間地占用鎖。
- 如果有多鎖, 如獲得順序是ABC連環扣, 釋放順序也應該是ABC。
- 線程錯誤返回時應該釋放它所獲得的鎖。
[pthreads線程(二) 線程同步--互斥量/鎖](http://www.cnblogs.com/dongsheng/p/4186358.html)
####2.線程信號量:解決多個線程在使用共享有限資源的同步問題。
線程的信號量與進程間通信中使用的信號量的概念是一樣,它是一種特殊的變量,它可以被增加或減少,但對其的關鍵訪問被保證是原子操作。如果一個程序中有多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。
而只有0和1兩種取值的信號量叫做二進制信號量,在這里將重點介紹。而信號量一般常用于保護一段代碼,使其每次只被一個執行線程運行。我們可以使用二進制信號量來完成這個工作。
信號量是一個計數器,用于控制訪問有限共享資源的線程數。
在操作系統中,信號量是一整數,在sem大于等于零時代表可供并發進程使用的資源實體數,但sem小于零時表示正在等待使用臨界區的進程數;
線程信號量,sem_t
>信號量的創建和初始化:
```c
#include
int sem_init (sem_t *sem, int pshared, unsigned int value);```
該函數初始化由sem指向的信號對象,設置它的共享選項,并給它一個初始的整數值。pshared控制信號量的類型,如果其值為0,就表示這個信號量是當前進程的局部信號量,否則信號量就可以在多個進程之間共享,value為sem的初始值。調用成功時返回0,失敗返回-1。
>信號量的獲取,以原子操作的方式將信號量的值--1。
```c
int sem_wait(sem_t * sem);```
sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1。
>信號量的釋放,以原子操作的方式將信號量的值++1。
```c
int sem_post(sem_t * sem);```
與sem_wait一樣,sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1。
>信號量的銷毀,用于對用完的信號量的清理。
```c
int sem_destroy (sem_t *sem);```
成功時返回0,失敗時返回-1。
[Linux多線程——使用信號量同步線程 ](http://blog.csdn.net/ljianhui/article/details/10813469/)
[線程同步(互斥鎖與信號量的作用與區別)](http://blog.csdn.net/tietao/article/details/7367827)
####3.原子操作
暨如果兩個線程企圖同時給一個信號量++1或--1,它們之間不會互相干擾。是最安全的操作,用鎖鎖在一起,或全執行,或全擱置。
#####課程使用各參數(暫時)安排
創建線程:
#include
//全局變量,可以是結構體,整型變量,等等。
void *thread_func(void *arg);//聲明線程函數
//主調函數內:
pthread_t thread_id; //注冊定義線程id
pthread_create(&thread_id, NULL, thread_func, NULL); //創建線程并指定線程的執行函數,最后一個參數為傳到線程函數的參數。
pthread_join(thread_id, NULL); // 等待指定線程的退出
//自定義線程函數內:
pthread_exit(NULL);//返回到join后一個參數。
線程互斥鎖:
pthread_mutex_t mutex;//全局定義線程互斥鎖
主調函數內:
注冊定義線程id
pthread_mutex_init(&mutex, NULL);//初始化線程互斥鎖
創建線程
等待線程退出
自定義線程函數內:
pthread_mutex_lock(&mutex);// 獲取互斥鎖
pthread_mutex_unlock(&mutex); // 釋放互斥鎖
!!互斥鎖的獲取和釋放必須成對出現!!
信號量:
sem_t sem;//全局定義信號量
void sig_handler(int signo);//聲明信號處理函數
主調函數內:
注冊定義線程id
signal(SIGINT, sig_handler);//注冊信號處理函數
sem_init(&sem, 0, 0);// 初始化信號量為0
創建線程
等待線程退出
sem_destroy(&sem);// 銷毀信號量
自定義線程函數內:
while(1)
sem_wait(&sem);// sem -1 對信號量進行wait(即-1)操作,如果<0,阻塞。
自定義信號處理函數內:
sem_post(&sem);//sem +1 對信號量進行post(即+1)操作,如果<=0,系統喚醒一個等待線程。
[進程同步互斥——不死鎖的哲學家問題](http://www.oschina.net/code/snippet_180916_7504)
[競爭與同步,互斥量,信號量,死鎖,條件變量,哲學家吃飯問題](http://www.th7.cn/system/lin/201509/134476.shtml)
####參考資料
[對Linux中多線程編程中pthread_join的理解](http://www.linuxidc.com/Linux/2013-09/89931.htm)
[Linux 線程操作函數技能總結](http://blog.csdn.net/shaderdx/article/details/50475982)
[pthread多線程編程的學習小結](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)
總結
以上是生活随笔為你收集整理的linux多线程编写哲学家,Linux系统编程(三) ------ 多线程编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux中的读目录,在linux中读取
- 下一篇: Linux通常把设备对象抽象为,linu