Linux多线程基础总结
一、線程參數(shù)傳遞
線程傳遞的參數(shù)類型為void*,傳遞方式有值傳遞、指針傳遞、引用傳遞
值傳遞:拷貝一份值給新的線程,多線程間不共享
址傳遞(指針傳遞):
- 連續(xù)創(chuàng)建多個線程中如果傳入的是同一變量地址,多線程共享這變量,由于哪個線程先運行是不確定的,變量的值是不可控的,并且會造成共享資源競爭問題,因為修改變量的值不是原子操作
- 如果主線程先結(jié)束,其內(nèi)存空間會被釋放,子進(jìn)程中的指針就成了野指針,可想而知,繼續(xù)操作這塊內(nèi)存會造成意想不到的后果。
結(jié)果方案:定義的變量使用堆內(nèi)存分配,可以避免棧內(nèi)存釋放,也可以使用全局變量
二、線程分離
線程分離后,線程退出自動釋放全部資源:pthread_detach()
三、線程資源的回收
非分離狀態(tài)的線程才可以被join回收資源
阻塞回收:pthread_join()
非阻塞回收:pthread_tryjoin_np()
限時阻塞回收:pthread_timedjoin_np()
四、線程清理函數(shù)
線程終止的時候,可以調(diào)用清理函數(shù)釋放資源,入棧和出棧函數(shù)必須成對的出現(xiàn)
清理函數(shù)入棧:pthread_cleanup_push()
清理函數(shù)出棧:pthread_cleanup_pop()(0:出棧不執(zhí)行 非零:出棧并執(zhí)行)
五、線程取消
線程在運行過程中可以調(diào)用被取消:pthread_cancel()
線程被取消后,join返回值為PTHREAD_CANCELED 即 -1
設(shè)置線程的取消狀態(tài):pthread_setcancelstate()
宏:PTHREAD_CANCEL_ASYNCHRONOUS: 立即取消
宏:PTHREAD_CANCEL_DEFERRED: 到達(dá)取消點(例如sleep())才取消
設(shè)置線程的取消點:pthread_testcancel()
六、線程與信號
七、線程安全
多個線程訪問共享資源(全局和靜態(tài)變量)的時候會沖突
例:定義全局變量int a=0; 線程1和線程2同時:循環(huán)執(zhí)行 a++ 一萬次 得到a的值小于2萬,a并不會自動到兩萬
三個概念:原子性、可見性、順序性
- 原子性:
一個操作(有可能包含有多個子操作)要么全部執(zhí)行(生效)要么全部都不執(zhí)行(都不生效)
CPU執(zhí)行指令:讀取指令、讀取內(nèi)存、執(zhí)行指令、寫回內(nèi)存
例(非原子性):第一不讀取指令:i++ 第二步:從內(nèi)存中讀取i的值 第三步:把i+1 第四步:把結(jié)果寫回內(nèi)存
-
可見性:
當(dāng)多個線程并發(fā)訪問共享變量時,一個線程對共享變量的修改,其它線程能夠立即看到。
CPU有高速緩存。每個線程讀取共享變量時,會將該變量從內(nèi)存加載到CPU的緩存中,修改該變量后,CPU會立即更新緩存,但不一定會立即將它寫回內(nèi)存。此時其它線程訪問該變量,從內(nèi)存中讀到的是舊數(shù)據(jù),而非第一個線程更新后的數(shù)據(jù)。 -
順序性:
程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。
CPU為了提高程序整體的執(zhí)行效率,可能會對代碼進(jìn)行優(yōu)化,按照更高效的順序執(zhí)行代碼。
CPU雖然并不保證完全按照代碼順序執(zhí)行,但它會保證程序最終的執(zhí)行結(jié)果和代碼順序執(zhí)行時的結(jié)果一致。
例:int a = 1; a = 2; a = 3; 編譯器對代碼優(yōu)化為int a = 3;
volatile關(guān)鍵字:保證可見性和禁止代碼優(yōu)化,但不是原子操作
解決線程安全問題:原子操作(c++原子類)、和線程同步(鎖)
原子操作:本質(zhì)是總線鎖
八、線程同步
1. 互斥鎖
等待鎖的時候,線程會休眠,不會消耗CPU,適合等待時間可能很長的場景
聲明鎖:
pthread_mutex_t mutex;-
普通鎖: PTHREAD_MUTEX_TIMED_NP
當(dāng)一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后按優(yōu)先級獲得鎖。
這種鎖策略保證了資源分配的公平性。 -
嵌套鎖:PTHREAD_MUTEX_RECURSIVE_NP
允許同一個線程對同一個鎖成功獲得多次,并通過多次unlock解鎖。
如果是不同線程請求,則在加鎖線程解鎖時重新競爭。 -
適應(yīng)鎖:PTHREAD_MUTEX_ADAPTIVE_NP
解鎖后,請求鎖的線程重新競爭。
2. 自旋鎖
循環(huán)的加測鎖是否可用,會消耗CPU,適合等待時間很短的場景
pthread_spinlock_t mutex; //聲明鎖 int pthread_spin_init(); //初始化鎖 int pthread_spin_lock(); //等待并加鎖 int pthread_spin_trylock(); //嘗試加鎖,不等待 int pthread_spin_unlock(); //解鎖 int pthread_spin_destroy(); //銷毀鎖4. 讀寫鎖
讀時共享,寫時單獨,適用于讀的次數(shù)遠(yuǎn)大于寫的場景
-
定義鎖:
pthread_rwlock_t mutex; //聲明鎖PTHREAD_RWLOCK_INITIALIZER //使用宏初始化鎖int pthread_rwlock_init(); //初始化鎖int pthread_rwlock_destroy(); //銷毀鎖 -
鎖屬性:
int pthread_rwlockattr_getpshared();//獲取讀寫鎖屬性 int pthread_rwlockattr_setpshared();//設(shè)置讀寫鎖屬性 PTHREAD_PROXESS_PRIVATE(單個線程私有) PTHREAD_PROCESS_SHARED(多線程共享) -
讀鎖:
int pthread_rwlock_rdlock(); //阻塞獲取讀鎖 int pthread_rwlock_tryrdlock(); //嘗試獲取讀鎖,不阻塞 int pthread_rwlock_timedrdlock(); //獲取讀鎖,帶超時機(jī)制 -
寫鎖:
int pthread_rwlock_wrlock(); //阻塞獲取寫鎖 int pthread_rwlock_trywrlock(); //嘗試獲取寫鎖,不阻塞 int pthread_rwlock_timedwrlock(); //獲取寫鎖,帶超時機(jī)制
注意:只有在不加鎖時,才能獲取到寫鎖。linux系統(tǒng)優(yōu)先考慮獲取讀鎖,獲取寫鎖的線程需要等待所有讀鎖釋放才能獲得到鎖
九、條件變量
pthread_cond_t cond; //聲明條件變量 PTHREAD_COND_INITIALIZER; //使用宏初始化條件變量 int pthread_cond_init(); //初始化條件變量 int pthread_cond_destroy(); //銷毀條件變量int pthread_cond_wait(); //等待被喚醒進(jìn)行加鎖 int pthread_cond_timedwait();//等待被喚醒進(jìn)行加鎖,帶超時機(jī)制int pthread_cond_signal(); //喚醒至少一個等待中的線程 int pthread_cond_broadcast();//喚醒全部等待中的線程int pthread_condattr_getpshared();//獲取共享屬性 int pthread_condattr_setpshared();//設(shè)置共享屬性(單個線程私有、多線程共享)十、信號量
多進(jìn)程的信號量可以用在多線程中,而多線程的信號量只能用于多線程中,多線程的信號量使用比較簡單
sem_t *sem; //聲明信號量 int sem_init(); //初始化信號量 int sem_destroy();//銷毀信號量int sem_wait(sem_t *sem); //信號量的P操作 int sem_trywait(sem_t *sem);//信號量的P操作,不阻塞 int sem_timedwait(); //信號量的P操作,帶超時機(jī)制 int sem_post(sem_t *sem); //信號量的V操作 int sem_getvalue(); //獲取信號量的值注意:以上幾種同步機(jī)制都能形成等待隊列,但是不是絕對公平的,當(dāng)前線程的cpu時間片還未使用完,獲取到鎖的概率會很大
十一、生產(chǎn)者消費者模型
十二、保證多線程程序的穩(wěn)定性
十三、多線程實現(xiàn)異步通訊
主線程創(chuàng)建socket連接,一個子線程負(fù)責(zé)發(fā)送,另一個子線程負(fù)責(zé)接收
總結(jié)
以上是生活随笔為你收集整理的Linux多线程基础总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 信号量及其操作函数
- 下一篇: Linux线程详解