UNIX高級環境編程 第十一、12章 線程同步及屬性

第十一、12章 線程及其控制

主要內容函數

  • 互斥量
    非遞歸互斥量
    遞歸互斥量spa

  • 讀寫鎖線程

  • 條件變量rest

  • 自旋鎖code

  • 屏障遞歸

互斥量

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);
//也能夠把互斥量設置爲常量PTHREAD_MUTEX_INITIALIZER(只適用於靜態分配的互斥量)進行初始化。
int pthread_mutex_destroy(pthread_mutex_t *mutex);

int pthread_mutex_lock(pthread_mutex_t *mutex); 
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//若是互斥量處於未鎖住狀態,則鎖住互斥量,不然返回EBUSY。
int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                             const struct timespec *restrict tsptr);

pthread_mutex_timedlock容許綁定線程阻塞時間,若是超過設定的時間點,pthread_mutex_timedlock不會對互斥量進行加鎖,而是返回錯誤碼ETIMEDOUT, 注意tsptr表示的是某個時間點,而不是一段時間長度接口

產生死鎖的四個條件進程

  • 互斥條件:一個資源每次只能被一個進程使用ip

  • 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的互斥資源保持不放內存

  • 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪

  • 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係

預防死鎖的幾個方法:

  • 對於每一個進程,將其須要的全部資源一次性分配,若失敗,系統收回其保持的全部資源(對應第二個條件)

  • 每一個進程或線程申請資源時,按照相同的順序(對應第四個條件)

互斥量屬性

主要內容

  • 進程共享屬性

    PTHREAD_PROCESS_PRIVATE (默認)
    PTHREAD_PROCESS_SHARED

  • 健壯屬性

    PTHREAD_MUTEX_STALLED (默認)
    PTHREAD_MUTEX_ROBUST

  • 類型屬性
    PTHREAD_MUTEX_NORMAL 默認
    PTHREAD_MUTEX_ERRORCHECK
    PTHREAD_MUTEX_RECURSIVE (遞歸鎖)
    PTHREAD_MUTEX_DEFAULT

int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared);

pshared能夠設置爲兩個值:PTHREAD_PROCESS_PRIVATE(默認),PTHREAD_PROCESS_SHARED
(從多個進程彼此之間共享的內存數據塊中分配的互斥量就能夠用於這些線程的同步)

int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict attr,int *restrict robust);
int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr,int robust);

PTHREAD_MUTEX_STALLED: 持有互斥量的進程終止時不須要採起特別的動做;

PTHREAD_MUTEX_ROBUST: 當進程P1的T1線程或其餘Pi進程持有鎖且終止時沒有釋放鎖時,P1中的T2線程此時利用
ptread_mutex_lock 函數獲取鎖時, T2線程不會阻塞且成功獲取鎖,但返回的值是EOWNERDEAD而不是0

注意: 當同一個進程內,若是一個線程退出時沒有釋放非健壯性鎖,那麼其餘的線程會被阻塞,而不是返回錯誤!!!

int pthread_mutex_consistent(pthread_mutex_t *mutex);

pthread_mutex_lock返回EOWNERDEAD時,須要調用 pthread_mutex_consistent 函數來清除這種狀態

int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr,int *restrict type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr,int type);

四種類型

clipboard.png
                       [Figure 0]

  • 情景一

使用遞歸鎖每每不是一個好主意,特別是和條件變量一塊兒使用時。以下圖所示,若線程A在第a步以前已經對mutex上鎖
那麼第b.1步並無真正解鎖,當執行到b.2時,線程A休眠等待線程B的信號,可是線程B第a步沒法獲取mutex,則產生了死鎖

clipboard.png

                       [Figure 1]

  • 情景二
    若mutex不是遞歸鎖,當func1調用func2時,顯然會產生死鎖。

clipboard.png

                       [Figure 2]
在不利用遞歸鎖狀況下,能夠採起以下方法避免死鎖:  
1.修改func2接口,指示mutex是否已經被調用者func1鎖定,當鎖定時,顯然無須再次上鎖.
2.生成一個func2_locked函數,相比func2, func2_locked無須第a步和第c步,而後,將func1和func2的第b步替換爲func2_locked函數

讀寫鎖

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_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrwrock(pthread_rwlock_t *rwlock);

當讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖以前,全部試圖對這個所加鎖的線程都會被阻塞。
當讀寫鎖是讀加鎖狀態時,全部試圖以讀模式對它進行加鎖的線程均可以獲得訪問權,
可是任何但願以寫模式對此進行加鎖的線程都會阻塞,直到全部的線程釋放它們的讀鎖爲止

讀寫鎖屬性

讀寫鎖的惟一屬性是進程共享屬性

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);

int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,
                                  int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr,int pshared);

條件變量

創建在互斥量上的同步: 先來先得(FCFS),先獲取鎖的線程或進程解鎖時,喚醒阻塞的相關線程或進程
創建在條件變量上的同步:人爲設定前後執行關係,如Figure 1所示,線程A的執行取決於線程B的喚醒

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 tsptr);
    若是超過到期時條件仍是沒有出現,則函數從新獲取互斥量,而後返回ETIMEDOUT

int pthread_cond_signal(pthread_cond_t *cond);
    至少能喚醒一個等待該條件的線程,
int pthread_cond_broadcast(pthread_cond_t *cond);
    能喚醒等待該條件的全部線程。

代碼展現:                
注意 「while(workq == NULL)」 的做用很是重要:
1)是否能夠用 if 取代 while :
若條件變量qready只是和變量workq對應,那麼能夠替換。若qready還和其餘變量(好比 workq2)對應,顯然不行,
由於線程被喚醒時便跳出if語句,繼續執行。可是這個線程被喚醒的緣由多是另外一個線程修改了workq2後調用pthread_cond_signal.
2)若條件變量qready只是和變量workq對應,那麼「while(workq == NULL)」是否能夠刪除:
絕對不能,考慮若enqueue_msg先被一個線程執行完畢(但此時沒有線程被喚醒),接着另一個線程執行缺乏「while(workq == NULL)」語句的process_msg函數時就會產生死鎖。
clipboard.png

-------------------------------------------[Figure 3]----------------------------------------------

自旋鎖

自旋鎖與互斥量相似,但它不是經過休眠使進程阻塞,而是在獲取鎖以前一直處於忙等(自旋)阻塞狀態。
自旋鎖可用於如下狀況:鎖被持有的時間短,並且線程並不但願在從新調度上花費太多的成本。

int pthread_spin_init(pthread_spinlock_t *lock,int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);

int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
    若沒法獲取鎖,當即返回EBUSY
int pthread_spin_unlock(pthread_spinlock_t *lock);

屏障

屏障是用戶協調多個線程並行工做的同步機制。屏障容許每一個線程阻塞,直到全部相關線程到達一點

int pthread_barrier_init(pthread_barrier_t *restrict barrier,
                            const pthread_barrierattr_t *restrict attr,
                                unsigned int count);
    最多有count - 1個線程阻塞
int pthread_barrier_destroy(pthread_barrier_t *barrier);
int pthread_barrier_wait(pthread_barrier_t *barrier);

屏障屬性

屏障屬性只有進程共享屬性

int pthread_barrierattr_init(pthread_barrierattr_t *attr);
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);

int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr,
                                   int *restrict pshared);
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr,int pshared)
相關文章
相關標籤/搜索