Linux多執行緒開發
阿新 • • 發佈:2022-05-12
一、執行緒
概念
執行緒(thread)是一種允許應用程式併發執行多個任務的機制。
執行緒之間共享全域性資料區,以及堆區。
程序是CPU分配資源的最小單位,執行緒是作業系統排程執行的最小單位。
執行緒是輕量級程序(LWP: Light Weight Process),在Linux環境下,其本質仍是程序
檢視指定的程序的LWP號:ps -Lf pid
區別
程序間的資訊難以共享,需要通過程序間通訊來進行資訊互動
fork的代價較高,儘管已經使用了寫時複製,讀時共享技術;仍需要複製PCB資訊,如記憶體頁表,檔案描述符表等。
執行緒只需要將資料複製到共享變數(全域性或堆)中即可共享資訊。
建立執行緒比建立程序快10倍甚至更多。無需寫時複製,也無需複製頁表。
NPTL,或稱為 Native Thread Library,是Linux執行緒的一個新實現,滿足了POSIX需求,提升了效能和穩定性。
檢視當前pthread版本庫:getconf GNU_LIBPTHREAD_VERSION
執行緒操作
預設main函式所在的執行緒為主執行緒,其餘為子執行緒。
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); - 功能:建立一個子執行緒 - 引數: - thread:傳出引數,執行緒建立成功後,子執行緒的執行緒ID被寫到該變數中。 - attr : 設定執行緒的屬性,一般使用預設值,NULL - start_routine : 函式指標,這個函式是子執行緒需要處理的邏輯程式碼 - arg : 給第三個引數使用,傳參 - 返回值: 成功:0 失敗:返回錯誤號。這個錯誤號和之前errno不太一樣。 獲取錯誤號的資訊: char * strerror(int errnum); void pthread_exit(void *retval); 功能:終止一個執行緒,在哪個執行緒中呼叫,就表示終止哪個執行緒 引數: retval:需要傳遞一個指標,作為一個返回值,可以在pthread_join()中獲取到。pthread_t pthread_self(void); 功能:獲取當前的執行緒的執行緒ID
int pthread_equal(pthread_t t1, pthread_t t2); 功能:比較兩個執行緒ID是否相等 不同的作業系統,pthread_t型別的實現不一樣,有的是無符號的長整型,有的 是使用結構體去實現的。 #include <pthread.h> int pthread_join(pthread_t thread, void **retval); - 功能:和一個已經終止的執行緒進行連線 回收子執行緒的資源 這個函式是阻塞函式,呼叫一次只能回收一個子執行緒 一般在主執行緒中使用 - 引數: - thread:需要回收的子執行緒的ID - retval: 接收子執行緒退出時的返回值 - 返回值: 0 : 成功 非0 : 失敗,返回的錯誤號 #include <pthread.h> int pthread_detach(pthread_t thread); - 功能:分離一個執行緒。被分離的執行緒在終止的時候,會自動釋放資源返回給系統。 1.不能多次分離,會產生不可預料的行為。 2.不能去連線一個已經分離的執行緒,會報錯。 - 引數:需要分離的執行緒的ID - 返回值: 成功:0 失敗:返回錯誤號 #include <pthread.h> int pthread_cancel(pthread_t thread); - 功能:取消執行緒(讓執行緒終止) 取消某個執行緒,可以終止某個執行緒的執行, 但是並不是立馬終止,而是當子執行緒執行到一個取消點,執行緒才會終止。 取消點:系統規定好的一些系統呼叫,我們可以粗略的理解為從使用者區到核心區的切換,這個位置稱之為取消點。
執行緒屬性
int pthread_attr_destroy(pthread_attr_t *attr); - 釋放執行緒屬性的資源
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); - 獲取執行緒分離的狀態屬性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); - 設定執行緒分離的狀態屬性
二、執行緒同步
概念
執行緒通過全域性變數來共享資訊,十分便捷;但也會導致問題,多個執行緒同時修改同一變數。
臨界區:訪問共享資源的程式碼片段,且為原子操作。
執行緒同步:一次只能有一個執行緒操作記憶體。
互斥量
互斥量(mutex,mutual exclusion)可以保證對共享資源的原子訪問(同時僅有一個執行緒訪問資源)
使用時:先加鎖,再訪問共享資源,最後解鎖
API介紹
互斥量的型別 pthread_mutex_t int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); - 初始化互斥量 - 引數 : - mutex : 需要初始化的互斥量變數 - attr : 互斥量相關的屬性,NULL - restrict : C語言的修飾符,被修飾的指標,不能由另外的一個指標進行操作。 pthread_mutex_t *restrict mutex = xxx; pthread_mutex_t * mutex1 = mutex;int pthread_mutex_destroy(pthread_mutex_t *mutex); - 釋放互斥量的資源
int pthread_mutex_lock(pthread_mutex_t *mutex); - 加鎖,阻塞的,如果有一個執行緒加鎖了,那麼其他的執行緒只能阻塞等待
int pthread_mutex_trylock(pthread_mutex_t *mutex); - 嘗試加鎖,如果加鎖失敗,不會阻塞,會直接返回。
int pthread_mutex_unlock(pthread_mutex_t *mutex); - 解鎖 死鎖 多個程序在執行過程中,因爭奪共享資源而造成的一種互相等待的現象。 原因:忘記釋放鎖;重複加鎖;多執行緒多鎖,搶佔資源
讀寫鎖
有時,我們希望共享讀,獨佔寫;這就是讀寫鎖的需求。
特點
- 如果有其它執行緒讀資料,則允許其它執行緒執行讀操作,但不允許寫操作。
- 如果有其它執行緒寫資料,則其它執行緒都不允許讀、寫操作。
- 寫是獨佔的,寫的優先順序高。
API介紹
讀寫鎖的型別 pthread_rwlock_t int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);條件變數
條件變數的型別 pthread_cond_t int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); - 等待,呼叫了該函式,執行緒會阻塞。 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); - 等待多長時間,呼叫了這個函式,執行緒會阻塞,直到指定的時間結束。 int pthread_cond_signal(pthread_cond_t *cond); - 喚醒一個或者多個等待的執行緒 int pthread_cond_broadcast(pthread_cond_t *cond); - 喚醒所有的等待的執行緒訊號量
訊號量的型別 sem_t int sem_init(sem_t *sem, int pshared, unsigned int value); - 初始化訊號量 - 引數: - sem : 訊號量變數的地址 - pshared : 0 用線上程間 ,非0 用在程序間 - value : 訊號量中的值int sem_destroy(sem_t *sem); - 釋放資源
int sem_wait(sem_t *sem); - 對訊號量加鎖,呼叫一次對訊號量的值-1,如果值為0,就阻塞
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); int sem_post(sem_t *sem); - 對訊號量解鎖,呼叫一次對訊號量的值+1
int sem_getvalue(sem_t *sem, int *sval);
sem_t psem; sem_t csem; init(psem, 0, 8); init(csem, 0, 0);
producer() { sem_wait(&psem); sem_post(&csem) }
customer() { sem_wait(&csem); sem_post(&psem) }