線程
在單進程環(huán)境下使用多線程執(zhí)行多個任務(wù)。一個進程的所有線程可以訪問該進程的資源泄隔。當然也需要涉及處理一致性問題麻蹋。
線程標識
進程ID pid_t數(shù)據(jù)類型表示 ,而線程用 pthread_t 數(shù)據(jù)類型表示诈悍。
不同平臺對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可能是未初始化的值给猾。值得注意。
線程終止和線程等待
線程使用一般的 return ((void *) 1) 或者 pthread_exit((void *) 1),即退出狀態(tài)碼颂跨,在其他線程中可以通過 pthread_join函數(shù)獲得該線程的退出碼敢伸。
線程取消
pthread_cancel( ID )僅僅是提出要求,該線程并不等待毫捣。
線程可以安排退出時需要調(diào)用的函數(shù)详拙,稱為線程清理處理程序帝际,記錄程序記錄在棧中,執(zhí)行順序和注冊順序相反饶辙。
線程同步
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)象涌矢,若無外力作用掖举,它們都將無法推進下去。
常見的死鎖原因:
當同一線程對同一互斥量加鎖兩次也會產(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)于為什么傳遞一個互斥鎖,書上解釋為:
(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)度上花費太多成本吩案。
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í)行過程中由其他程序改變梯啤。