linux高級環(huán)境編程-線程

線程

在單進程環(huán)境下使用多線程執(zhí)行多個任務(wù)。一個進程的所有線程可以訪問該進程的資源泄隔。當然也需要涉及處理一致性問題麻蹋。

線程標識

進程ID pid_t數(shù)據(jù)類型表示 ,而線程用 pthread_t 數(shù)據(jù)類型表示诈悍。

image.png

不同平臺對pthread_t 實現(xiàn)不同。

線程創(chuàng)建
#include<stdio.h>  
#include<pthread.h>  
#include<string.h>  
#include<sys/types.h>  
#include<unistd.h>  
pthread_t main_tid;  
void print_ids(const char *str)  
{  
    pid_t pid;      //進程id  
    pthread_t tid;  //線程id  
    pid = getpid();       //獲取當前進程id  
    tid = pthread_self(); //獲取當前線程id  
    printf("%s pid: %u tid: %u (0x%x)/n",  
                str,  
                (unsigned int)pid,  
                (unsigned int)tid,  
                (unsigned int)tid);  
}  
void *func(void *arg)  
{  
    print_ids("new  thread:");  
    return ((void *)0);  
}  
int main()  
{  
    int err;  
    err = pthread_create(&main_tid, NULL, func, NULL); //創(chuàng)建線程  
    if(err != 0){  
        printf("create thread error: %s/n",strerror(err));  
        return 1;  
    }  
    printf("main thread: pid: %u tid: %u (0x%x)/n",   
                (unsigned int)getpid(),  
                (unsigned int)pthread_self(),  
                (unsigned int)pthread_self());  
    print_ids("main thread:");  
    sleep(1);  
    return 0;  
}  

運行 gcc -Wall -o pthread_create pthread_create.c -lpthread
需要鏈接 pthread 庫文件
注意:主線程需要休眠兽埃,不然可能會退出導(dǎo)致新線程退出侥钳。二是使用pthread_self()函數(shù)獲取線程ID,而不是用main_tid柄错,用可以用舷夺,但可能會出問題,因為新線程在主線程調(diào)用pthread_creat返回之前就運行的話售貌,main_tid可能是未初始化的值给猾。值得注意。

線程終止和線程等待
image.png
image.png

線程使用一般的 return ((void *) 1) 或者 pthread_exit((void *) 1),即退出狀態(tài)碼颂跨,在其他線程中可以通過 pthread_join函數(shù)獲得該線程的退出碼敢伸。

image.png
線程取消
image.png

pthread_cancel( ID )僅僅是提出要求,該線程并不等待毫捣。

線程可以安排退出時需要調(diào)用的函數(shù)详拙,稱為線程清理處理程序帝际,記錄程序記錄在棧中,執(zhí)行順序和注冊順序相反饶辙。

image.png
image.png
線程同步
1 線程互斥鎖
/* Try locking a mutex.  */  
int pthread_mutex_trylock (pthread_mutex_t *__mutex);  
  
/* Lock a mutex.  */  
int pthread_mutex_lock (pthread_mutex_t *__mutex);  
  
/* Unlock a mutex.  */  
int pthread_mutex_unlock (pthread_mutex_t *__mutex); 

trylock和lock 最主要區(qū)別就是程序是否阻塞蹲诀。
上述程序調(diào)用之前需要對 __mutex 進行初始化,兩種初始化方式
1弃揽、靜態(tài)分配 pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
2脯爪、動態(tài)分配,該方式后面要釋放內(nèi)存 。mutexattr表示屬性矿微,暫不考慮痕慢。

int pthread_mutex_init (pthread_mutex_t *__mutex,\  
                        const pthread_mutexattr_t *__mutexattr);  
  
/* Destroy a mutex.  */  
int pthread_mutex_destroy (pthread_mutex_t *__mutex); 
避免死鎖

死鎖是指兩個或兩個以上的進程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象涌矢,若無外力作用掖举,它們都將無法推進下去。
常見的死鎖原因:


image.png

當同一線程對同一互斥量加鎖兩次也會產(chǎn)生死鎖娜庇。
還有個函數(shù) pthread_mutex_timedlock()可以指定超時時間塔次,就是等了多久還沒有等到鎖釋放就返回。

2 讀寫鎖 (待實例理解)

也需要初始化名秀,并且最后釋放

/* Initialize read-write lock  */  
 int pthread_rwlock_init (pthread_rwlock_t  *restrict rwlock,  
                                const pthread_rwlockattr_t *restrict  attr);  
  
/* Destroy read-write lock */  
extern int pthread_rwlock_destroy (pthread_rwlock_t *rwlock);    
                                             返回值:成功返回0励负,否則返回錯誤代碼  

讀寫鎖有三種狀態(tài),讀模式下加鎖匕得,寫模式下加鎖和 解鎖

/* 讀模式下加鎖  */  
int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock);    
/* 非阻塞的讀模式下加鎖  */  
int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock);  
 
/*  限時等待的讀模式加鎖 */  
int pthread_rwlock_timedrdlock (pthread_rwlock_t *restrict rwlock,const struct timespec * restrict  abstime);    
/* 寫模式下加鎖  */  
int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock);    
/* 非阻塞的寫模式下加鎖 */  
int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock);  
/* 限時等待的寫模式加鎖 */  
int pthread_rwlock_timedwrlock (pthread_rwlock_t *restrict rwlock,  const struct timespec *restrict  abstime);  

/* 解鎖 */  
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);  
                                             返回值:成功返回0继榆,否則返回錯誤代碼 
3 條件變量

條件變量與互斥量一同使用時,允許線程以無競爭的方式等待特定條件發(fā)生汁掠。

條件變量與互斥量不同略吨,互斥量是防止多線程同時訪問共享的互斥變量來保護臨界區(qū)。條件變量是多線程間可以通過它來告知其他線程某個狀態(tài)發(fā)生了改變调塌,讓等待在這個條件變量的線程繼續(xù)執(zhí)行晋南。通俗一點來講:設(shè)置一個條件變量讓線程1等待在一個臨界區(qū)的前面,當其他線程給這個變量執(zhí)行通知操作時羔砾,線程1才會被喚醒,繼續(xù)向下執(zhí)行偶妖。
條件變量必須和一個互斥鎖配合姜凄,以防止多個線程同時請求 pthread_cond_wait() (或 pthread_cond_timedwait())的競爭條件。

具體為什么要和互斥鎖一起使用:
互斥鎖一個明顯的缺點是他只有兩種狀態(tài):鎖定和非鎖定趾访。而條件變量通過允許線程阻塞和等待另一個線程發(fā)送信號的方法彌補了互斥鎖的不足态秧,他常和互斥鎖一起使用。使用時扼鞋,條件變量被用來阻塞一個線程申鱼,當條件不滿足時愤诱,線程會先被阻塞,然后解開互斥鎖捐友,等待條件變量發(fā)生變化淫半。一旦其他的某個線程改變了條件變量,會發(fā)出一個signal通知相應(yīng)的條件變量喚醒一個或多個正被此條件變量阻塞的線程匣砖。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足科吭。一般說來,條件變量被用來進行線承間的同步猴鲫。
可以總結(jié)為:條件變量用在某個線程需要在某種條件才去保護它將要操作的臨界區(qū)的情況下对人,從而避免了線程不斷輪詢檢查該條件是否成立而降低效率的情況,這是實現(xiàn)了效率提高拂共。
在條件滿足時牺弄,自動退出阻塞,再加鎖進行操作宜狐。
Linux下C編程的條件變量:條件變量是線程中的東西猖闪,就是等待某一條件的發(fā)生和信號一樣  以下是說明:  條件變量使我們可以睡眠等待某種條件出現(xiàn)。條件變量是利用線程間共享的全局變量進行同步的一種機制肌厨,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起培慌;另一個線程使"條件成立"(給出條件成立信號)。為了防止競爭柑爸,條件變量的使用總是和一個互斥鎖結(jié)合在一起吵护。

初始化

1、宏常量初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER

2 表鳍、函數(shù)初始化和回收

 #include <pthread.h>
 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *restrict cond)
等待和通知環(huán)境變量 (*restrict作用馅而?)
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

關(guān)于為什么傳遞一個互斥鎖,書上解釋為:

image.png

(why)
值得注意的就是傳遞的時間是絕對值譬圣,如等三分鐘瓮恭,是當前時間加上三分鐘后再轉(zhuǎn)換為timespec結(jié)構(gòu)。
pthread_cond_broadcast 和pthread_cond_signal函數(shù)可以通知線程條件已經(jīng)滿足厘熟,一個喚醒所有線程屯蹦,一個至少喚醒一個。

深入理解

互斥量實質(zhì)是競爭關(guān)系绳姨,多個線程操作一個資源時為了避免混亂登澜,對操作的部分加鎖,其他線程等待鎖飘庄。而條件變量本質(zhì)是等待關(guān)系脑蠕,條件是可以自定義的,在一個線程中等待條件的成立,而另一線程中改變條件谴仙,當條件滿足時線程使用broadcast和signal函數(shù)通知迂求。關(guān)于互斥鎖,也很容易理解晃跺,因為兩個線程對同一全局變量(條件)訪問揩局,一個線程判斷條件時需要加鎖。具體解釋如下:

pthread_mutex_t mutex;  ///< 互斥鎖
pthread_cond_t  cond;   ///< 條件變量
bool test_cond = false;
/// TODO 初始化mutex和cond
 
/// thread 1:
pthread_mutex_lock(&mutex);            ///< 1
while (!test_cond)
{
    pthread_cond_wait(&cond, &mutex);  ///< 2,3
}
pthread_mutex_unlock(&mutex);          ///< 4
RunThread1Func();
 
/// thread 2:
pthread_mutex_lock(&mutex);            ///< 5
test_cond = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);          ///< 6
 
/// TODO 銷毀mutex和cond

(1)條件變量的使用過程中哼审,最為關(guān)鍵的一點是互斥鎖的使用谐腰。細心的朋友應(yīng)該發(fā)現(xiàn)了,我在上面的例子中標了1涩盾、2十气、3、4春霍、5砸西、6個標號。在這里1址儒、4芹枷、5、6都是正常的lock/unlock莲趣,2鸳慈、3是需要特別說明的。2是進入pthread_cond_wait后的喧伞,pthread_cond_wait調(diào)的pthread_mutex_unlock走芋,這樣做的目的是為了保證在thread1阻塞wait后,thread2獲取同一把鎖mutex的時候潘鲫,能夠正常獲任坛选(即5,6)溉仑。3是thread1被喚醒后挖函,要退出pthead_cond_wait之前,pthread_cond_wait調(diào)的pthread_mutex_lock浊竟,這樣做的目的是為了把mutex的控制權(quán)還給調(diào)用pthread_cond_wait的線程(即thread1)怨喘。

自旋鎖

自旋鎖與互斥鎖類似,但他不是通過休眠使進程阻塞逐沙,而是在獲取鎖之前一直忙等(自旋)阻塞狀態(tài)哲思,可以用于:鎖持有時間短,線程不希望在重新調(diào)度上花費太多成本吩案。

image.png

image.png

http://www.cnblogs.com/yuuyuu/p/5140875.html
http://blog.csdn.net/fengbingchun/article/details/48579725 理解

屏障 barrier

協(xié)調(diào)多個線程并行工作的同步機制,允許每個線程等待帝簇,直到所有合作線程達到同一個點徘郭,然后從該點繼續(xù)運行靠益,pthread_join()就是一種barrier。
補充:
文件和文件鎖
https://liwei.life/2016/07/31/file_and_filelock/

信號量

信號量是在互斥鎖的基礎(chǔ)上升級残揉,互斥鎖只允許一個線程進入臨界區(qū)胧后,而信號量允許多個線程同時進入臨界區(qū) 。簡單說抱环,信號量就相當于一些令牌環(huán)壳快,令牌環(huán)個數(shù)表示允許的線程個數(shù),當然信號量保證了操作的原子性镇草,原子性表示一件事情要不全部執(zhí)行完且過程不中斷眶痰,要不就不執(zhí)行。原子性表示變量不會再執(zhí)行過程中由其他程序改變梯啤。

線程安全與可重入函數(shù)

https://blog.csdn.net/feiyinzilgd/article/details/5811157

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竖伯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子因宇,更是在濱河造成了極大的恐慌七婴,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察滑,死亡現(xiàn)場離奇詭異打厘,居然都是意外死亡,警方通過查閱死者的電腦和手機贺辰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門户盯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人魂爪,你說我怎么就攤上這事先舷。” “怎么了滓侍?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵蒋川,是天一觀的道長。 經(jīng)常有香客問我撩笆,道長捺球,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任夕冲,我火速辦了婚禮氮兵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歹鱼。我一直安慰自己泣栈,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著南片,像睡著了一般掺涛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疼进,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天薪缆,我揣著相機與錄音,去河邊找鬼伞广。 笑死拣帽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的嚼锄。 我是一名探鬼主播减拭,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灾票!你這毒婦竟也來了峡谊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤刊苍,失蹤者是張志新(化名)和其女友劉穎既们,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體正什,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡啥纸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了婴氮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斯棒。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖主经,靈堂內(nèi)的尸體忽然破棺而出荣暮,到底是詐尸還是另有隱情,我是刑警寧澤罩驻,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布穗酥,位于F島的核電站,受9級特大地震影響惠遏,放射性物質(zhì)發(fā)生泄漏砾跃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一节吮、第九天 我趴在偏房一處隱蔽的房頂上張望抽高。 院中可真熱鬧,春花似錦透绩、人聲如沸翘骂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雏胃。三九已至请毛,卻和暖如春志鞍,著一層夾襖步出監(jiān)牢的瞬間瞭亮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工固棚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留统翩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓此洲,卻偏偏與公主長得像厂汗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呜师,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay閱讀 1,610評論 0 52
  • linux線程同步 信號燈:與互斥鎖和條件變量的主要不同在于"燈"的概念娶桦,燈亮則意味著資源可用,燈滅則意味著不可用...
    鮑陳飛閱讀 684評論 0 2
  • 簡介 線程創(chuàng)建 線程屬性設(shè)置 線程參數(shù)傳遞 線程優(yōu)先級 線程的數(shù)據(jù)處理 線程的分離狀態(tài) 互斥鎖 信號量 一 線程創(chuàng)...
    第八區(qū)閱讀 8,554評論 1 6
  • 線程基礎(chǔ) 線程是進程的一個執(zhí)行單元汁汗,執(zhí)行一段程序片段衷畦,線程共享全局變量;線程的查看可以使用命令或者文件來進行查看知牌;...
    秋風弄影閱讀 736評論 0 0
  • 線程 在linux內(nèi)核那一部分我們知道祈争,線程其實就是一種特殊的進程,只是他們共享進程的文件和內(nèi)存等資源角寸,無論如何對...
    大雄good閱讀 664評論 0 2