Linux線程-Mutex互斥鎖和信號量互斥鎖的筆記
多線程開發在 Linux 平臺上已經有成熟的 pthread 庫支持。其涉及的多線程開發的最基本概念主要包含三點:線程,互斥鎖,條件。其中,線程操作又分線程的創建,退出,等待 3 種。互斥鎖則包括 4 種操作,分別是創建,銷毀,加鎖和解鎖。條件操作有 5 種操作:創建,銷毀,觸發,廣播和等待。其他的一些線程擴展概念,如信號燈等,都可以通過上面的三個基本元素的基本操作封裝出來。詳細請見下表:
線程創建API
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否則返回錯誤編號
當pthread_create成功返回時,由tidp指向的內存單元被設置為新創建線程的線程ID。attr參數用于定制各種不同的線程屬性,暫可以把它設置為NULL,以創建默認屬性的線程。
新創建的線程從start_rtn函數的地址開始運行,該函數只有一個無類型指針參數arg。如果需要向start_rtn函數傳遞的參數不止一個,那么需要把這些參數放到一個結構中,然后把這個結構的地址作為arg參數傳入。
2. 線程退出
單個線程可以通過以下三種方式退出,在不終止整個進程的情況下停止它的控制流:
1)線程只是從啟動例程中返回,返回值是線程的退出碼。
2)線程可以被同一進程中的其他線程取消。
3)線程調用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
rval_ptr是一個無類型指針,與傳給啟動例程的單個參數類似。進程中的其他線程可以通過調用pthread_join函數訪問到這個指針。
3. 線程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否則返回錯誤編號
調用這個函數的線程將一直阻塞,直到指定的線程調用pthread_exit、從啟動例程中返回或者被取消。如果例程只是從它的啟動例程返回i,rval_ptr將包含返回碼。如果線程被取消,由rval_ptr指定的內存單元就置為PTHREAD_CANCELED。
可以通過調用pthread_join自動把線程置于分離狀態,這樣資源就可以恢復。如果線程已經處于分離狀態,pthread_join調用就會失敗,返回EINVAL。
如果對線程的返回值不感興趣,可以把rval_ptr置為NULL。在這種情況下,調用pthread_join函數將等待指定的線程終止,但并不獲得線程的終止狀態。
4. 線程脫離
一個線程或者是可匯合(joinable,默認值),或者是脫離的(detached)。當一個可匯合的線程終止時,它的線程ID和退出狀態將留存到另一個線程對它調用pthread_join。脫離的線程卻像守護進程,當它們終止時,所有相關的資源都被釋放,我們不能等待它們終止。如果一個線程需要知道另一線程什么時候終止,那就最好保持第二個線程的可匯合狀態。
pthread_detach函數把指定的線程轉變為脫離狀態。
#include <pthread.h>
int pthread_detach(pthread_t thread);
// 返回:若成功返回0,否則返回錯誤編號
本函數通常由想讓自己脫離的線程使用,就如以下語句:
pthread_detach(pthread_self());
5. 線程ID獲取及比較
#include <pthread.h>
pthread_t pthread_self(void);
// 返回:調用線程的ID
對于線程ID比較,為了可移植操作,我們不能簡單地把線程ID當作整數來處理,因為不同系統對線程ID的定義可能不一樣。我們應該要用下邊的函數:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
// 返回:若相等則返回非0值,否則返回0
對于多線程程序來說,我們往往需要對這些多線程進行同步。同步(synchronization)是指在一定的時間內只允許某一個線程訪問某個資源。而在此時間內,不允許其它的線程訪問該資源。我們可以通過互斥鎖(mutex),條件變量(condition variable)和讀寫鎖(reader-writer lock)來同步資源。在這里,我們暫不介紹讀寫鎖。
與互斥鎖有關API
互斥量(mutex)從本質上來說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成后釋放互斥量上的鎖。對互斥量進行加鎖后,任何其他試圖再次對互斥量加鎖的線程將會被阻塞直到當前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運行狀態,第一個變為可運行狀態的線程可以對互斥量加鎖,其他線程將會看到互斥鎖依然被鎖住,只能回去等待它重新變為可用。在這種方式下,每次只有一個線程可以向前運行。
在設計時需要規定所有的線程必須遵守相同的數據訪問規則。只有這樣,互斥機制才能正常工作。操作系統并不會做數據訪問的串行化。如果允許其中的某個線程在沒有得到鎖的情況下也可以訪問共享資源,那么即使其它的線程在使用共享資源前都獲取了鎖,也還是會出現數據不一致的問題。
互斥變量用pthread_mutex_t數據類型表示。在使用互斥變量前必須對它進行初始化,可以把它置為常量PTHREAD_MUTEX_INITIALIZER(只對靜態分配的互斥量),也可以通過調用pthread_mutex_init函數進行初始化。如果動態地分配互斥量(例如通過調用malloc函數),那么在釋放內存前需要調用pthread_mutex_destroy。
1. 創建及銷毀互斥鎖
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t mutex);
// 返回:若成功返回0,否則返回錯誤編號
要用默認的屬性初始化互斥量,只需把attr設置為NULL。
2. 加鎖及解鎖
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t mutex);
int pthread_mutex_trylock(pthread_mutex_t mutex);
int pthread_mutex_unlock(pthread_mutex_t mutex);
// 返回:若成功返回0,否則返回錯誤編號
線程Mutex互斥鎖例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
//由于線程共享進程的資源和地址空間,因此在對這些資源進行操作時,必須考慮到線程間資源訪問的惟一性問題
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //快速互斥鎖
int lock_var;
time_t end_time;
void pthread1 ( void *arg );
void pthread2 ( void *arg );
int main ( int argc, char *argv[] )
{
pthread_t id1,id2;
int ret;
end_time = time ( NULL ) +10;
ret=pthread_create ( &id1,NULL, ( void * ) pthread1, NULL );
if ( ret!=0 )
perror ( "pthread cread1" );
ret=pthread_create ( &id2,NULL, ( void * ) pthread2, NULL );
if ( ret!=0 )
perror ( "pthread cread2" );
pthread_join ( id1,NULL );
pthread_join ( id2,NULL );
pthread_mutex_destroy(&mutex);
exit ( 0 );
}
void pthread1 ( void *arg )
{
int i;
while ( time ( NULL ) < end_time )//time()取得當前時間
{
if ( pthread_mutex_lock ( &mutex ) !=0 )
{
perror ( "pthread_mutex_lock" );
pthread_exit((void *)-1);
}
else
printf ( "pthread1 lock\n" );
for ( i=0;i<2;i++ )
{
sleep (1 );
lock_var++;
printf("pthread1 lock_var=%d\n",lock_var);
}
if ( pthread_mutex_unlock ( &mutex ) !=0 )
{
perror ( "pthread_mutex_unlock" );
}
else
printf ( "pthread1 unlock\n" );
sleep ( 1 );
}
}
void pthread2 ( void *arg )
{
int ret;
int i;
while ( time ( NULL ) < end_time )
{
//*
if((ret= pthread_mutex_lock ( &mutex ))!=0) //阻塞
{
perror("can't get lock\n");
pthread_exit((void *)-1);
}
// */
/*
ret=pthread_mutex_trylock ( &mutex );//非阻塞
if ( ret==EBUSY )
printf ( "pthread2:the variable is locked by pthread1\n" );
//*/
else if(ret==0)
{
printf ( "pthread2 got lock\n");
for ( i=0;i<2;i++ )
{
sleep (1);
lock_var++;
printf("pthread2 lock_var=%d\n",lock_var);
}
if ( pthread_mutex_unlock ( &mutex ) !=0 )
{
perror ( "pthread_mutex_unlock" );
}
else
printf ( "pthread2 unlock\n" );
}
sleep (1 );
}
}
信號量互斥鎖例子
//用信號量實現三個線程的互斥操作,且它們的執行順序是線程1-》2--》3--》1--》2--》3.。。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>
int lock_var;
time_t end_time;
sem_t sem1,sem2,sem3;
void pthread1(void *arg);
void pthread2(void *arg);
void pthread3(void *arg);
int main(int argc, char *argv[])
{
pthread_t id1,id2,id3;
pthread_t mon_th_id;
int ret;
end_time = time(NULL)+30;
ret=sem_init(&sem1,0,1);
ret=sem_init(&sem2,0,0);
ret=sem_init(&sem3,0,0);
if(ret!=0)
{
perror("sem_init");
}
ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);
if(ret!=0)
perror("pthread cread1");
ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
if(ret!=0)
perror("pthread cread2");
ret=pthread_create(&id3,NULL,(void *)pthread3, NULL);
if(ret!=0)
perror("pthread cread2");
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
exit(0);
}
void pthread1(void *arg)
{
int i;
while(time(NULL) < end_time){
sem_wait(&sem1);
for(i=0;i<2;i++){
sleep(1);
lock_var++;
printf("pthread1----lock_var=%d\n",lock_var);
}
printf("pthread1:lock_var=%d\n",lock_var);
sem_post(&sem2);
sleep(1);
}
}
void pthread2(void *arg)
{
int nolock=0;
int ret;
while(time(NULL) < end_time){
sem_wait(&sem2);
printf("pthread2:pthread2 got lock;lock_var=%d\n",lock_var);
sem_post(&sem3);
sleep(3);
}
}
void pthread3(void *arg)
{
int nolock=0;
int ret;
while(time(NULL) < end_time){
sem_wait(&sem3);
printf("pthread3:pthread3 got lock;lock_var=%d\n",lock_var);
sem_post(&sem1);
sleep(3);
}
}
智能推薦
Linux系統編程之線程控制及線程間同步互斥機制:POSIX信號量和互斥鎖
聲明:侵刪 文章目錄 1. 線程和進程的區別 2.prhread線程庫相關API 3.線程間的同步和互斥 4.互斥鎖 5.互斥鎖例程 6.POISX信號量 [System V 信號量參考此文](https://blog.csdn.net/qq_43921241/article/details/103123667) 7.POSIX信號量例程 1. 線程和進程的區別 進程 :每個進程有自己獨立的地址空...
線程的同步控制---信號量、互斥鎖、條件變量
一、線程同步 原因:線程之間擁有許多共享資源,當此資源被多個線程進行訪問時容易造成數據錯誤,程序崩潰,所以需要對線程的臨界資源進行同步控制。 同步:多進程和多線程訪問臨界資源時,需要進行同步控制。多線程或者多進程的執行并不會完全的并行運行,有可能主線程需要等待函數線程的某些條件的發生。 多線程之間臨界資源: 全局數據 堆區數據文件描述符 線程之間的同步控制方式: 3.1信號量 #include&l...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
猜你喜歡
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...