線程和線程同步

gcc編譯包含線程的源代碼需要加上-lpthread開關。
線程的優點:需要多任務時,節省程序運行的時空間消耗。
線程的缺點:各線程之間共享數據,不安全。
一個進程中的各線程有獨立的寄存器和棧,其他空間都共享,包括進程打開的文件和信號處理。
#include <pthread.h>
int pthread_create(pthread_t* restrict thread, const pthread_attr_t attr, void* startroutine(void), void* argv)
attr傳入NULL表示使用默認屬性。
void pthread_exit(void* retval)
終止線程。線程不能通過exit終止。如果一個線程調用了exit,整個進程將終止。
子線程默認爲非分離狀態,即受到主線程控制,子線程結束後不會釋放資源,需要在主線程中用pthread_join(thread1, NULL)接手並釋放。
同步機制
1.互斥量
類型:pthread_mutex_t
擁有加鎖和未加鎖兩種狀態。
PTHREAD_MUTEX_INITIALIZER
靜態創建互斥量。
用法:pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restrict attr)
動態創建互斥量。
int pthread_mutex_lock(pthread_mutex_t * mutex)
給互斥量mutex加鎖,若互斥量已經加鎖,則阻塞該線程。
int pthread_mutex_trylock(pthread_mutex_t * mutex)
嘗試給互斥量加鎖,若不能加上,errno設置爲EBUSY,並返回EBUSY;若能加上,加鎖且返回0;錯誤返回其他值。
int pthread_mutex_unlock(pthread_mutex_t * mutex)
給互斥量解鎖。成功返回0,失敗返回-1。
int pthread_mutex_destroy(pthread_mutex_t *mutex)
消除互斥量。
死鎖產生的原因:
  1. 系統資源不足時內核態線程優先級不夠,導致搶佔資源失敗

  2. 同一線程對一個互斥量兩次加鎖

  3. 線程A對A互斥量加鎖,B對B互斥量加鎖,然後A對B互斥量加鎖,B對A互斥量加鎖,則兩個線程產生死鎖

產生死鎖的四個必要條件:
  • 互斥條件:一個資源在同一時間只能被一個線程或進程佔用。

  • 請求與保持條件:線程或進程在請求被阻塞的情況下保持已擁有的資源不釋放。

  • 不剝奪條件:不能強行剝奪線程或進程的資源。

  • 循環等待條件:若干個線程或進程頭尾相接地循環等待對方釋放資源。

不滿足以上四個條件中的任一個,則不會發生死鎖。
2.讀寫鎖
類型:pthread_rwlock_t
有三個狀態:讀鎖、寫鎖、未加鎖。
加了寫鎖後任何線程都不能再加鎖,加了讀鎖後任何線程都可以對它加讀鎖,但不能加寫鎖。
適合讀比較頻繁、寫較少的情況,例如數據庫。
PTHREAD_RWLOCK_INITIALIZER
靜態創建讀寫鎖。用法與互斥量相似。
int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)
加讀鎖,要注意判斷返回值,因爲加讀鎖的次數可能有限制,因此可能失敗。
int pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)
加寫鎖,無需判斷返回值。
int pthread_rwlock_unlock(pthread_rwlock_t * rwlock)
解鎖,讀鎖和寫鎖都解開。
int pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)
嘗試加讀鎖。
int pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)
嘗試加寫鎖。
int pthread_rwlock_destroy(pthread_rwlock_t * rwlock)
消除讀寫鎖。
3.條件變量
類型:pthread_cond_t
與互斥量一起使用,在達到條件時喚醒被阻塞的線程。
PTHREAD_COND_INITIALIZER
靜態創建條件變量,與互斥量相似。
int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cattr)
初始化條件變量。若cattr置爲NULL,則創建缺省的條件變量,否則按cattr設定屬性。
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
解鎖給定的mutex,並使當前線程阻塞在cond上,直到被喚醒爲止。
爲了避免意外喚醒(假喚醒),一般將wait放在循環中,循環的終止條件就是喚醒線程的條件。
int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * timeout)
帶有等待時間的wait函數,若時間到還未被喚醒,則返回ETIMEDOUT。
int pthread_cond_signal(pthread_cond_t * cond)
int pthread_cond_broadcast(pthread_cond_t *cond)
喚醒阻塞在這個條件變量上的線程。signal喚醒一個線程,broadcast則喚醒所有線程。
如果沒有線程在等待,這兩個函數的喚醒信號將不會被保留。
4.信號量
類型:sem_t
struct semid_ds{
     struct ipc-perm sem_perm; //許可權限
     time_t sem_otime; //最後一次操作時間
     time_t sem_ctime; //最後一次調用semctl時間
     unsigned long sem_nsems; //信號量個數
};
信號量計數器大於等於0時代表該信號量集中剩餘的系統資源,小於0時代表等待使用該信號量集中資源的線程數。
信號量計數器的值只能通過PV操作來改變,P操作將當前進程由運行狀態轉爲阻塞狀態,V操作將一個被阻塞的進程喚醒。
PV操作是原子操作。
#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value)
初始化信號量,pshared參數決定該信號量是否能在進程間共享,Linux系統下一般取0;value參數決定信號量計數器的初始值。
int sem_wait(sem_t *sem)
若sem計數器不大於0,則阻塞當前線程;否則使sem減一併繼續執行。
事實上,這個函數試圖對信號量“加鎖”(信號量小於等於0視爲加鎖狀態),如果未能成功加鎖,則不會返回到調用它的線程中,直到成功加鎖或者被信號解除爲止。這與互斥量的加鎖函數原理相同。
int sem_trywait(sem_t *sem)
類似wait,但若不能加鎖會立刻返回,不阻塞線程。
int sem_post(sem_t *sem)
將sem加一,併發出信號喚醒阻塞的線程,讓它們前來接受資源。
int sem_getvalue(sem_t *sem)
得到sem的值。
int sem_destroy(sem_t *sem)
消除信號量。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章