亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

線程控制之同步屬性

2024-06-28 13:28:22
字體:
來源:轉載
供稿:網友
線程控制之同步屬性

就像線程具有屬性一樣,線程的同步對象(如互斥量、讀寫鎖、條件變量、自旋鎖和屏障)也有屬性。http://www.CUOXin.com/nufangrensheng/p/3521654.html中介紹了自旋鎖的唯一的一個屬性,本篇介紹互斥量、讀寫鎖、條件變量及屏障的屬性。

1、互斥量屬性

用pthread_mutexattr_init初始化pthread_mutexattr_t結構,用pthread_mutexattr_destroy來對該結構進行回收。

#include <pthread.h>int pthread_mutexattr_init( pthread_mutexattr_t *attr );int pthread_mutexattr_destroy( pthread_mutexattr_t *attr );返回值:若成功則返回0,否則返回錯誤編號

pthread_mutexattr_init函數用默認的互斥量屬性初始化pthread_mutexattr_t結構。值得注意的兩個屬性是進程共享屬性和類型屬性。POSIX.1中,進程共享屬性是可選的,可以通過檢查系統中是否定義了_POSIX_THREAD_PROCESS_SHARED符號來判斷這個平臺是否支持進程共享這個屬性,也可以在運行時把_SC_THREAD_PROCESS_SHARED參數傳給sysconf函數進行檢查。雖然這個選項并不是遵循POSIX標準的操作系統必須提供的,但是Single UNIX Specification要求遵循XSI標準的操作系統支持這個選項。

在進程中,多個線程可以訪問同一個同步對象,這是默認的行為。在這種情況下,進程共享互斥量屬性需要設置為PTHREAD_PROCESS_PRIVATE

存在這樣一種機制:允許相互獨立的多個進程把同一個內存區域映射到它們各自獨立的地址空間中。就像多個線程訪問共享數據一樣,多個進程訪問共享數據通常也需要同步。如果進程共享互斥量屬性設置為PTHREAD_PROCESS_SHARED,從多個進程共享的內存區域中分配的互斥量就可以用于這些進程的同步

可以使用pthread_mutexattr_getpshared函數查詢pthread_mutexattr_t結構,得到它的進程共享屬性;可以用pthread_mutexattr_setpshared函數修改進程共享屬性。

#include <pthread.h>int pthread_mutexattr_getpshared( const pthread_mutexattr_t *restrict attr, int *restrict pshared );int pthread_mutexattr_setpshared( pthread_mutexattr_t *attr, int pshared );返回值:若成功則返回0,否則返回錯誤編號

進程共享互斥量屬性設為PTHREAD_PROCESS_PRIVATE時,允許pthread線程庫提供更加有效的互斥量實現,這在多線程應用程序中是默認的情況。在多個進程共享多個互斥量的情況下,pthread線程庫可以限制開銷較大的互斥量實現。

類型互斥量屬性控制著互斥量的特性。POSIX.1定義了四種類型。PTHREAD_MUTEX_NORMAL類型是標準的互斥量類型,并不做任何特殊的錯誤檢查或死鎖檢測。PTHREAD_MUTEX_ERRORCHECK互斥量類型提供錯誤檢查。

PTHREAD_MUTEX_RECURSIVE互斥量類型允許同一線程在互斥量解鎖之前對該互斥量進行多次加鎖。用一個遞歸互斥量維護鎖的計數。在解鎖的次數和加鎖的次數不相同的情況下不會釋放鎖。所以如果對一個遞歸互斥量加鎖兩次,然后對它解鎖一次,這個互斥量依然處于加鎖狀態,在對它再次解鎖以前不能釋放該鎖。

最后,PTHREAD_MUTEX_DEFAULT類型可以用于請求默認語義。操作系統在實現它的時候可以把這種類型自由地映射到其他類型。例如,在linux中,這種類型映射為普通的互斥量類型。

四種類型的行為如表12-4所示?!安徽加脮r解鎖”這一欄指的是一個線程對被另一個線程加鎖的互斥量進行解鎖的情況;“在已解鎖時解鎖”這一欄指的是當一個線程對已經解鎖的互斥量進行解鎖時將會發生的情況,這通常是編碼錯誤所致。

                                                 表12-4 互斥量類型行為

28152501-cd6249d79dc344248e27681bfac78703

可以用pthread_mutexattr_gettype函數得到互斥量類型屬性,用pthread_mutexattr_settype函數修改互斥量類型屬性。

#include <pthread.h>int pthread_mutexattr_gettype( const pthread_mutexattr_t * restrict attr, int *restrict type );int pthread_mutexattr_settype( pthread_mutexattr_t *attr, int type );兩者的返回值都是:若成功則返回0,否則返回錯誤編號

http://www.CUOXin.com/nufangrensheng/p/3521654.html中曾介紹過,互斥量可用于保護與條件變量關聯的條件。在阻塞線程之前,pthread_cond_wait和pthread_cond_timedwait函數釋放與條件相關的互斥量,這就允許其他線程獲取互斥量、改變條件、釋放互斥量并向條件變量發送信號。既然改變條件時必須占有互斥量,所以使用遞歸互斥量并不是好的辦法。如果遞歸互斥量被多次加鎖,然后用在調用pthread_cond_wait函數中,那么條件永遠都不會得到滿足,因為pthread_cond_wait所作的解鎖操作并不能釋放互斥量。

如果需要把現有的單線程接口放到多線程環境中,遞歸互斥量是非常有用的,但由于程序兼容性的限制,不能對函數接口進行修改。然而由于遞歸鎖的使用需要一定技巧,它只應在沒有其他可行方案的情況下使用。

實例

使用遞歸鎖的情況:

main(){    func1(x);    ......    func2(x);}func1(){    pthread_mutex_lock(x->lock);    ......    func2(x);    ......    pthread_mutex_unlock(x->lock);}func2(){    pthread_mutex_lock(x->lock);    ......    pthread_mutex_unlock(x->lock);}

  上面的代碼解釋了遞歸鎖看似解決并發問題的情況。假設func1和func2是函數庫中現有的函數,其接口不能改變,因為存在調用這兩個接口的應用程序,而且應用程序不能改動。

為了保持接口跟原來相同,可以把互斥量嵌入到數據結構中,把這個數據結構的地址(x)作為參數傳入。這種方案只有在為該數據結構提供了分配函數時才可行,所以應用并不知道數據結構的大小(假設在其中增加互斥量后必須擴大該數據結構的大?。?/p>

如果在最初定義數據結構時,預留了足夠的可填充字段,允許把一些填充字段替換成互斥量,那么這種方法也是可行的。不過,大多數程序員并不善于預測未來,所以這不是普遍可行的經驗。

如果func1和func2函數都必須操作這個結構,而且可能會有多個線程同時訪問該數據結構,那么func1和func2必須在操作數據以前對互斥量加鎖。當func1必須調用func2時,如果互斥量不是遞歸類型,那么就會出現死鎖。如果能在調用func2之前釋放互斥量,在func2返回后重新獲取互斥量,那么就可以避免使用遞歸互斥量,但這也給其他的線程提供了機會,其他的線程可能在func1執行期間得到互斥量的控制權,修改這個數據結構。

避免使用遞歸鎖的情況:

main(){    func1(x);    ......    func2(x);}func1(){    pthread_mutex_lock(x->lock);    ......    func2_locked(x);    ......    pthread_mutex_unlock(x->lock);}func2(){    pthread_mutex_lock(x->lock);    func2_locked(x);    pthread_mutex_unlock(x->lock);}

上面的代碼顯示了這種情況下使用遞歸互斥量的另一種替代方法。通過提供func2函數的私有版本(稱之為func2_locked函數),可以保持func1和func2函數接口不變,并且避免使用遞歸互斥量。要調用func2_locked函數,必須占有嵌入到數據結構中的互斥量,這個數據結構的地址是作為參數傳入的。func2_locked的函數體包含func2的副本(原來的、沒有加入互斥量的func2),func2現在只是用以獲取互斥量,調用func2_locked,最后釋放互斥量。

如果并不一定要保持庫函數接口不變,就可以在每個函數中另外再加一個參數,以表明這個結構是否被調用者鎖定。但是,如果可能的話,保持接口不變通常是更好的選擇,這樣可以避免實現過程中人為加入的東西對原有接口產生不良影響。

提供函數的加鎖版本和不加鎖版本,這樣的策略在簡單的情況下通常是可行的。在比較復雜的情況下,例如庫需要調用庫以外的函數,而且可能會再次回調庫中的函數時,就需要依賴遞歸鎖。

實例

程序清單12-2解釋了有必要使用遞歸互斥量的另一種情況。這里,有一個“超時”(timeout)函數,它允許另一個函數可以安排在未來的某個時間運行。假設線程并不是很昂貴的資源,可以為每個未決的超時函數創建一個線程。線程在時間未到時將一直等待,時間到了以后就調用請求的函數。

#include "apue.h"#include <pthread.h>#include <time.h>#include <sys/time.h>extern int makethread(void *(*)(void *), void *);struct to_info{    void    (*to_fn)(void *);    /* function */    void    *to_arg;        /* argument */    struct    timespec to_wait;    /* time to wait */};#define SECTONSEC    1000000000    /* seconds to nanoseconds */#define USECTONSEC    1000        /* microseconds to nanoseconds */void *timeout_helper(void *arg){    struct to_info    *tip;        tip = (struct to_info *)arg;    nanosleep(&tip->to_wait, NULL);    (*tip->to_fn)(tip->to_arg);    return(0);}void timeout(const struct timespec *when, void (*func)(void *), void *arg){    struct timespec now;    struct timeval    tv;    struct to_info    *tip;    int        err;        gettimeofday(&tv, NULL);    now.tv_sec = tv.tv_sec;    now.tv_nsec = tv.tv_usec * USECTONSEC;    if((when->tv_sec > now.tv_sec) ||       (when->tv_sec == now.tv_sec && when->tv_nsec > now.tv_nsec))    {        tip = malloc(sizeof(struct to_info));        if(tip != NULL)        {                tip->to_fn = func;            tip->to_arg = arg;            tip->to_wait.tv_sec = when->tv_sec - now.tv_sec;            if(when->tv_nsec >= now.tv_nsec)            {                tip->to_wait.tv_nsec = when->tv_nsec - now.tv_nsec;            }            else            {                tip->to_wait.tv_sec--;                tip->to_wait.tv_nsec = SECTONSEC - now.tv_nsec + when->tv_nsec;            }            err = makethread(timeout_helper, (void *)tip);            if(err == 0)                return;        }    }    /*    * We get here if (a) when <= now, or (b) malloc fails, or    * (c) we can't make a thread, so we just call the function now.    */    (*func)(arg);}pthread_mutexattr_t attr;pthread_mutex_t mutex;void retry(void *arg){    pthread_mutex_lock(&mutex);    /* perform retry steps ... */    pthread_mutex_unlock(&mutex);}int main(void){    int        err, condition, arg;    struct timespec when;    if((err == pthread_mutexattr_init(&attr)) != 0)        err_exit(err, "pthread_mutexattr_init failed");    if((err == pthread_mutexattr_settype(&attr,        PTHREAD_MUTEX_RECURSIVE)) != 0)        err_exit(err, "can't set recursive type");    if((err == pthread_mutex_init(&mutex, &attr)) != 0)        err_exit(err, "can't create recursive mutex");    /* ... */    pthread_mutex_lock(&mutex);    /* ... */    if(condition)    {        /* calculate target time "when" */        timeout(&when, retry, (void *)arg);    }    /* ... */    pthread_mutex_unlock(&mutex);    /* ... */    exit(0);}

如果不能創建線程,或者安排函數運行的時間已過,問題就出現了。在這種情況下,要從當前環境中調用之前請求運行的函數,因為函數要獲取的鎖和現在占有的鎖是同一個,除非該鎖是遞歸的,否則就會出現死鎖。

這里使用程序清單12-1中的makethread函數以分離狀態創建線程。希望函數在未來的某個時間運行,而且不希望一直等待線程結束。

可以調用sleep等待超時到達,但它提供的時間粒度是秒級的,如果希望等待的時間不是整數秒,需要用nanosleep(2)函數,它提供了類似的功能。

timeout的調用者需要占有互斥量來檢查條件,并且把retry函數安排為原子操作。retry函數試圖對同一個互斥量進行加鎖,因此,除非互斥量是遞歸的,否則如果timeout函數直接調用retry就會導致死鎖。

2、讀寫鎖屬性

讀寫鎖與互斥量類似,也具有屬性。用pthread_rwlockattr_init初始化pthread_rwlockattr_t結構,用pthread_rwlockattr_destroy回收結構。

#include <pthread.h>int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);兩者的返回值都是:若成功則返回0,否則返回錯誤編號

讀寫鎖支持的唯一屬性是進程共享屬性,該屬性與互斥量的進程共享屬性相同。就像互斥量的進程共享屬性一樣,用一對函數來讀取和設置讀寫鎖的進程共享屬性。

#include <pthread.h>int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t * restrict attr, int *restrict pshared);int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);兩者的返回值都是:若成功則返回0,否則返回錯誤編號

3、條件變量屬性

條件變量也有屬性。與互斥量和讀寫鎖類似,有一對函數用于初始化和回收條件變量屬性。

#include <pthread.h>int pthread_condattr_init(pthread_condattr_t *attr);int pthread_condattr_destroy(pthread_condattr_t *attr);兩者的返回值都是:若成功則返回0,否則返回錯誤編號

與其他的同步原語一樣,條件變量支持進程共享屬性。

#include <pthread.h>int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr, int *restrict pshared);int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared);兩者的返回值都是:若成功則返回0,否則返回錯誤編號

4、屏障屬性

屏障也具有屬性。我們可以使用pthread_barrierattr_init函數初始化一個屏障屬性,使用pthread_barrierattr_destroy函數回收屏障屬性。

#include <pthread.h>int pthread_barrierattr_init(pthread_barrierattr_t *attr);int pthread_barrierattr_destroy(pthread_barrierattr_t attr);兩個函數的返回值都是:若成功則返回0,否則返回錯誤編號

唯一的一個屏障屬性是進程共享屬性。

#include <pthread.h>int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr, int *restrict pshared);int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int *pshared);兩個函數的返回值都是:若成功則返回0,否則返回錯誤編號

 

本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人免费网站在线观看| 欧美精品在线极品| 日韩精品中文字幕视频在线| 伊人伊人伊人久久| 久久久99久久精品女同性| 亚洲国产精品999| 97久久久免费福利网址| 日韩成人在线播放| 久久免费视频网| 欧美成人中文字幕| 国产精品入口尤物| 色偷偷偷综合中文字幕;dd| 欧美日本亚洲视频| 欧美一级淫片播放口| 久久精品国产69国产精品亚洲| 日本欧美一级片| 日韩小视频网址| 国产精品日日做人人爱| 久久99久久99精品免观看粉嫩| 国产精品久久久| 亚洲欧美精品一区二区| 色无极影院亚洲| 国产精品久久久久久中文字| 91精品久久久久久久久久久久久久| 精品国产91久久久久久老师| 国产+成+人+亚洲欧洲| 亚洲精品女av网站| 国产mv久久久| 日本a级片电影一区二区| 91免费看片网站| 91大神在线播放精品| 欧美性色视频在线| 亚洲欧美一区二区三区在线| 欧美xxxx做受欧美| 欧美专区国产专区| 久久久久久久久久久免费| 日韩成人高清在线| 久久久噜噜噜久久| 久久视频在线视频| 成人综合网网址| 97成人超碰免| 日韩av手机在线| 色偷偷亚洲男人天堂| 97久久精品人人澡人人爽缅北| 国产香蕉97碰碰久久人人| 国产午夜精品美女视频明星a级| 国产精品成人品| 国产精品国产三级国产专播精品人| 国产精品自拍偷拍| 日韩欧美国产黄色| 欧美激情a∨在线视频播放| 日本国产高清不卡| 国产精品www网站| 国产精品视频1区| 少妇高潮久久77777| 九九热r在线视频精品| 久热99视频在线观看| 欧美肥老妇视频| 色婷婷**av毛片一区| 国产精品一区二区在线| 日韩中文字幕在线观看| 日韩在线国产精品| 久久亚洲精品网站| 91精品国产91久久久久久最新| 国产成人高清激情视频在线观看| 久久97精品久久久久久久不卡| 成人做爽爽免费视频| 精品国产一区二区三区久久狼黑人| 久久91亚洲精品中文字幕奶水| 91精品国产91久久久久福利| 韩剧1988在线观看免费完整版| 色在人av网站天堂精品| 欧美成人免费全部| 国产精品美女在线| 青青草原成人在线视频| 亚洲精品中文字幕有码专区| 亚洲xxxxx| 久久久久久欧美| 欧美极品欧美精品欧美视频| 亚洲国产日韩精品在线| 亚洲欧美日韩高清| 成人在线精品视频| 91久久久久久久一区二区| 精品电影在线观看| 欧美性xxxxxxx| 精品国产一区二区三区四区在线观看| 亚洲一区美女视频在线观看免费| 国内成人精品视频| 91成人免费观看网站| 国产精品私拍pans大尺度在线| 91在线视频免费| 久久久精品日本| 91欧美精品午夜性色福利在线| 性色av一区二区咪爱| 色综合视频一区中文字幕| 国产精品一久久香蕉国产线看观看| 久久久久久久亚洲精品| 红桃视频成人在线观看| 日韩精品极品毛片系列视频| 久久精品久久精品亚洲人| 欧美日韩高清在线观看| 国产精品高清在线观看| 欧美大片免费看| 在线观看国产精品淫| 国产亚洲精品久久久优势| 成人久久精品视频| 欧美电影在线观看完整版| 亚洲精品成人久久电影| 狠狠色噜噜狠狠狠狠97| 日韩美女在线观看| 国产精品91久久久久久| 狠狠色香婷婷久久亚洲精品| 欧美精品久久久久久久| 国产91网红主播在线观看| 欧美日韩国产麻豆| 久久99精品久久久久久青青91| 最近2019年好看中文字幕视频| 欧美孕妇孕交黑巨大网站| 亚洲色图18p| 亚洲www在线观看| 国产精品www| 亚洲视频欧美视频| 91国偷自产一区二区三区的观看方式| 欧美在线欧美在线| 久久精视频免费在线久久完整在线看| 97精品视频在线观看| 久久久久久com| 91在线免费网站| 91精品中文在线| 日韩电影网在线| 国产午夜精品麻豆| 久久国产精品久久国产精品| 欧美激情国产日韩精品一区18| 国产精品视频一区二区三区四| 日韩高清免费观看| 九九热r在线视频精品| 欧美黑人性猛交| 亚洲国产私拍精品国模在线观看| 国产美女久久精品香蕉69| 亚洲国产精品女人久久久| 欧美小视频在线观看| 久久久精品国产一区二区| 日韩av最新在线观看| 国产成人精品电影久久久| 中文字幕亚洲第一| 亚洲国产精品电影在线观看| 亚洲成人aaa| 最新中文字幕亚洲| 91国产精品电影| 992tv在线成人免费观看| 日韩欧美大尺度| 91在线观看免费高清| 国产精品欧美激情在线播放| 日韩精品视频在线免费观看| 欧美整片在线观看| 精品视频中文字幕| 91精品视频免费| 日韩成人久久久| 日韩精品在线免费观看| 国产精品私拍pans大尺度在线| 日韩av色在线| 色婷婷综合成人| 久久精品国产久精国产思思| 国产欧美一区二区三区视频|