日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

發(fā)布時間:2024/4/17 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
上篇文章也蠻好,線程同步之條件變量與互斥鎖的結(jié)合: http://www.cnblogs.com/charlesblc/p/6143397.html? 現(xiàn)在有這篇文章: http://blog.csdn.net/goodluckwhh/article/details/8564319 POSIX定義了一系列同步對象用于同步和互斥。
同步對象是內(nèi)存中的變量屬于進程中的資源,可以按照與訪問數(shù)據(jù)完全相同的方式對其進行訪問。默認情況下POSIX定義的這些同步對象具有進程可見性,即同步對象只對定義它的進程可見;但是通過修改同步對象的屬性可以使得同步對象對不同的進程可見,具體的做法是: 修改同步對象的屬性為PTHREAD_PROCESS_SHARED 在進程的特殊內(nèi)存區(qū)域--共享內(nèi)存中創(chuàng)建同步對象 這樣創(chuàng)建的同步對象將對共享該共享內(nèi)存的所有進程可見,這些進程可以使用該同步對象進行同步互斥。
其中設(shè)置共享對象的屬性為PTHREAD_PROCESS_SHARED是為了告訴系統(tǒng)該共享對象是跨越進程的,不僅僅對創(chuàng)建它的進程可見;但是僅有這一個條件顯然無法滿足不同進程使用該同步對象的需求,因為每個進程的地址空間是獨立的,位于一個進程的普通內(nèi)存區(qū)域中的對象是無法被其它進程所訪問的,能滿足這一要求的內(nèi)存區(qū)域是共享內(nèi)存,因而同步對象要在進程的共享內(nèi)存區(qū)域內(nèi)創(chuàng)建。
同步對象還可以放在文件中。同步對象可以比創(chuàng)建它的進程具有更長的生命周期。 POSIX定義的同步對象包括:
  • 互斥鎖
  • 條件變量
  • 自旋鎖
  • 讀寫鎖
  • 信號量
  • 對于這些同步對象,有一些共同點:
  • 每種類型的同步對象都有一個init的API,它完成該對象的初始化,在初始化過程中會分配該同步對象所需要的資源(注意是為支持這種鎖而需要的資源,不包括表示同步對象的變量本身所需要的內(nèi)存)
  • 每種類型的同步對象都一個destory的API,它完成與init相反的工作
  • 對于使用動態(tài)分配內(nèi)存的同步對象,在使用它之前必須先調(diào)用init
  • 在釋放使用動態(tài)分配內(nèi)存的同步對象所使用的內(nèi)存時,必須先調(diào)用destory釋放系統(tǒng)為其申請的資源
  • 每種同步對象的默認作用范圍都是進程內(nèi)部的線程,但是可以通過修改其屬性為PTHREAD_PROCESS_SHARED并在進程共享內(nèi)存中創(chuàng)建它的方式使其作用范圍跨越進程范圍
  • 無論是作用于進程內(nèi)的線程,還是作用于不同進程間的線程,真正參與競爭的都是線程(對于不存在多個線程的進程來說就是其主線程),因而討論都基于線程來
  • 這些同步對象都是協(xié)作性質(zhì)的,相當于一種君子協(xié)定,需要相關(guān)線程主動去使用,無法強制一個線程必須使用某個同步對象
  • 總體上來說,可以將它們分為兩類:?

  • 第一類是互斥鎖、讀寫鎖、自旋鎖,它們主要是用來保護臨界區(qū)的,也就是主要用于解決互斥問題的,當嘗試上鎖時大體上有兩種情況下會返回:上鎖成功或出錯,它們不會因為出現(xiàn)信號而返回。另外解鎖只能由鎖的擁有者進行。
  • 第二類是條件變量和信號量,它們提供了異步通知的能力,因而可以用于同步和互斥。但是二者又有區(qū)別:
    • 信號量可以由發(fā)起P操作的線程發(fā)起V操作,也可以由其它線程發(fā)起V操作;但是條件變量一般要由其它線程發(fā)起signal(即喚醒)操作
    • 由于條件變量并沒有包含任何需要檢測的條件的信息,因而對這個條件需要用其它方式來保護,所以條件變量需要和互斥鎖一起使用,而信號量本身就包含了相關(guān)的條件信息(一般是資源可用量),因而不需要和其它方式一起來使用
    • 類似于三種鎖,信號量的P操作要么成功返回,要么失敗返回,不會因為出現(xiàn)信號而返回;但是條件變量可能因為出現(xiàn)信號而返回,這也是因為它沒包含相關(guān)的條件信息而導致的。(注意:無名信號量也會被信號中斷,見:http://www.cnblogs.com/charlesblc/p/6142868.html)

    一、互斥鎖

    如果有多個線程在等待一個互斥鎖,則在持有互斥鎖的線程釋放鎖后鎖將被等待鎖的線程中具有最高優(yōu)先級的那個獲得,如果最高優(yōu)先級線程有多個,則這些線程中誰將獲得鎖是不確定的。

    1)初始化互斥鎖

    如果互斥鎖變量是靜態(tài)的則可以直接用PTHREAD_MUTEX_INITIALIZER來初始化它,比如:
    static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER
    如果互斥鎖變量是動態(tài)分配的,則必須在使用它之前用pthread_mutex_init來初始化它.

  • int?pthread_mutex_init(pthread_mutex_t?*mp,?const?pthread_mutexattr_t?*mattr);?成功返回0,其它返回值表示出錯 ?
  • pthread_mutex_init用于初始化互斥鎖,如果mattr為NULL則用缺省值初始化由mp所指向的互斥鎖,否則使用指定的mattr初始化互斥鎖。
    使用PTHREAD_MUTEX_INITIALIZER與動態(tài)分配具有null 屬性的 pthread_mutex_init等效,不同之處在于PTHREAD_MUTEX_INITIALIZER 宏不進行錯誤檢查。
    如果使用pthread_mutex_init初始化互斥鎖,并且指定的mattr具有PTHREAD_MUTEX_ROBUST_NP屬性,則互斥鎖所使用的內(nèi)存必須在調(diào)用pthread_mutex_init之前被清0.
    在有線程正在使用互斥鎖時,不能重新初始化互斥鎖或銷毀它。

    1)使互斥保持一致

    如果一個互斥鎖的持有者沒有釋放該鎖退出了,則在默認情況下當其它線程再去獲取這個鎖的時候,就會阻塞從而造成死鎖。可以更改互斥鎖的屬性來改變這種默認的方式:
      pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
      pthread_mutexattr_setrobust_np(&mattr,PTHREAD_MUTEX_ROBUST_NP);
    通過設(shè)置鎖的上面兩個屬性,互斥鎖就不再具有默認的行為,當一個鎖的owner死掉后,其它線程再去獲取這個鎖的時候,不會被阻塞,而是會獲得這個錯,但是同時會得到一個EOWNERDEAD的錯誤。
    然后獲得鎖的線程可以嘗試處理這個錯誤:

  • 首先調(diào)用pthread_mutex_consistent_np函數(shù)來恢復該鎖的一致性,
  • 然后調(diào)用pthread_mutex_unlock來解鎖
  • 接下來在調(diào)用加鎖
  • 這樣該鎖的行為就恢復正常了。
    如果pthread_mutex_consistent_np在恢復鎖的一致性時候沒有成功,步驟c就不能再執(zhí)行了,鎖也不能被使用了,而且接下來的線程在獲取鎖都無法獲得該鎖,而是只能得到返回值ENOTRECOVERABLE。
    如果獲取某個鎖的時候得到了ENOTRECOVERABLE的錯誤,就意味這這個鎖不能被使用了,此時只能調(diào)用pthread_mutex_destroy銷毀互斥鎖然后再調(diào)用pthread_mutex_int重新初始化該互斥鎖,之后才能再使用該互斥鎖。

    ?

    3)鎖定(獲取)互斥鎖

    pthread_mutex_lock可以鎖定指定的互斥鎖。
    當它返回時,該互斥鎖已被鎖定,調(diào)用它的線程就獲得了這個互斥鎖。如果該互斥鎖已被另一個線程鎖定和擁有,則調(diào)用線程將阻塞,直到該互斥鎖變?yōu)榭捎脼橹埂?/p>

    4)解除互斥鎖鎖定(釋放互斥鎖)

    pthread_mutex_unlock可以解除指定互斥鎖的鎖定即釋放互斥鎖。

    5)嘗試鎖定(獲取)互斥鎖

    pthread_mutex_trylock可以嘗試鎖定指定的互斥鎖。
    pthread_mutex_trylock是 pthread_mutex_lock的非阻塞版本。如果 mutex 所引用的互斥對象當前被任何線程鎖定,則將立即返回該調(diào)用。否則,該互斥鎖將被鎖定,調(diào)用線程成為其持有者。

    6)銷毀互斥鎖

    pthread_mutex_destroy可以銷毀與指定的互斥鎖相關(guān)聯(lián)的任何狀態(tài)。

    7)初始化互斥鎖屬性對象

    互斥鎖具有一些屬性,通過修改這些屬性可以控制鎖的一些行為。缺省的互斥鎖屬性及其值如下:

    • pshared: ? ? ? ? ?PTHREAD_PROCESS_PRIVATE
    • type: ? ? ? ? ? ? ? ? ?PTHREAD_MUTEX_DEFAULT
    • protocol: ? ? ? ? ? PTHREAD_PRIO_NONE
    • prioceiling: ? ? ? –?
    • robustness: ? ?PTHREAD_MUTEX_STALLED_NP

    可以用pthread_mutexattr_init將與互斥鎖對象相關(guān)聯(lián)的屬性初始化為其缺省值。pthread_mutexattr_init的參數(shù)類型實際上是opaque的,其中包含一個由系統(tǒng)分配的屬性對象。該函數(shù)執(zhí)行過程中會為屬性對象分配所需的內(nèi)存,因而如果未通過pthread_mutexattr_destroy銷毀互斥鎖屬性對象時就會導致內(nèi)存泄漏。
    對于互斥鎖屬性對象,必須首先通過調(diào)用pthread_mutexattr_destroy將其銷毀,才能重新初始化該對象。

    ?

    9)設(shè)置/獲取互斥鎖的作用域?qū)傩?/h3>

    函數(shù)pthread_mutexattr_setpshared用來設(shè)置互斥鎖的作用域。
    互斥鎖變量可以是進程專用的變量,也可以是跨越進程邊界的變量。
    范圍屬性的取值及其含義:

    • PTHREAD_PROCESS_SHARED:具有該屬性的互斥鎖可以在多個進程中的線程之間共享。
    • PTHREAD_PROCESS_PRIVATE:只有創(chuàng)建本互斥鎖的線程所在的進程內(nèi)的線程才能夠使用該互斥鎖變量。該值是缺省值。

    函數(shù)pthread_mutexattr_getpshared可用來返回由 pthread_mutexattr_setpshared設(shè)置的互斥鎖變量的范圍。

    10)設(shè)置/獲取互斥鎖的類型屬性

    pthread_mutexattr_settype用來設(shè)置指定互斥鎖的類型屬性。類型屬性的缺省值為 PTHREAD_MUTEX_DEFAULT。
    互斥鎖的類型及其行為:

  • PTHREAD_MUTEX_NORMAL:不提供死鎖檢測。嘗試重新鎖定互斥鎖會導致死鎖。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或未鎖定,則將產(chǎn)生不確定的行為。
  • PTHREAD_MUTEX_ERRORCHECK:提供錯誤檢查。如果某個線程嘗試重新鎖定的互斥鎖已經(jīng)由該線程鎖定,則將返回錯誤。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯誤。
  • PTHREAD_MUTEX_RECURSIVE:該互斥鎖會保留鎖定計數(shù)這一概念。線程首次成功獲取互斥鎖時,鎖定計數(shù)會設(shè)置為 1。線程每重新鎖定該互斥鎖一次,鎖定計數(shù)就增加 1。線程每解除鎖定該互斥鎖一次,鎖定計數(shù)就減小 1。 鎖定計數(shù)達到 0 時,該互斥鎖即可供其他線程獲取。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯誤。
  • PTHREAD_MUTEX_DEFAULT:嘗試以遞歸方式鎖定該互斥鎖將產(chǎn)生不確定的行為。對于不是由調(diào)用線程鎖定的互斥鎖,如果嘗試解除對它的鎖定,則會產(chǎn)生不確定的行為。如果嘗試解除鎖定尚未鎖定的互斥鎖,則會產(chǎn)生不確定的行為。
  • ?

    對比下信號,信號可以做到通知其它線程某件事發(fā)生了,接收信號的線程只需要注冊一個信號處理函數(shù),然后信號發(fā)生后該處理函數(shù)就會被系統(tǒng)調(diào)用,一旦該函數(shù)被調(diào)用了就意味著注冊時關(guān)聯(lián)的信號所代表的事情發(fā)生了。但要注意:

    ?

  • POSXI要求多線程應(yīng)用中信號處理程序必須在應(yīng)用的多個線程之間共享(即在一個進程的多個線程之間共享),因而對于同一個進程中的多個線程來說它們必須共享信號處理程序,信號處理程序無法確定信號是被發(fā)給誰的
  • 使用信號時只需要注冊信號處理程序即可,不需要創(chuàng)建某種同步對象,而使用條件變量需要創(chuàng)建同步對象,如果要在進程間進行同步和互斥還對條件變量的作用域和屬性有要求
  • 有權(quán)限的任何用戶的任何程序都可以發(fā)送信號給一個線程,而使用條件變量時,相關(guān)的線程必須可以訪問同步對象
  • ?

    4)在指定的時間之前阻塞

    pthread_cond_timedwait的用法與 pthread_cond_wait的用法基本相同,區(qū)別在于在由abstime指定的時間之后不再被阻塞。
    pthread_cond_reltimedwait_np與pthread_cond_timedwait基本相同,它們唯一的區(qū)別在于pthread_cond_reltimedwait_np使用相對時間間隔而不是將來的絕對時間作為其最后一個參數(shù)的值。
    類似于pthread_cond_wait,pthread_cond_reltimedwait_np和pthread_cond_timedwait也是取消點。

    ?

    1.基本概念

    自旋鎖是SMP架構(gòu)中的一種low-level的同步機制。
    當線程A想要獲取一把自選鎖而該鎖又被其它線程鎖持有時,線程A會在一個循環(huán)中自選以檢測鎖是不是已經(jīng)可用了。對于自選鎖需要注意:

    • 由于自旋時不釋放CPU,因而持有自旋鎖的線程應(yīng)該盡快釋放自旋鎖,否則等待該自旋鎖的線程會一直在那里自旋,這就會浪費CPU時間。
    • 持有自旋鎖的線程在sleep之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。(在內(nèi)核編程中,如果持有自旋鎖的代碼sleep了就可能導致整個系統(tǒng)掛起,最近剛解決了一個內(nèi)核中的問題就是由于持有自旋鎖時sleep了,然后導致所有的核全部掛起(是一個8核的CPU))

    使用任何鎖需要消耗系統(tǒng)資源(內(nèi)存資源和CPU時間),這種資源消耗可以分為兩類:

  • 建立鎖所需要的資源
  • 當線程被阻塞時鎖所需要的資源
  • 對于自旋鎖來說,它只需要消耗很少的資源來建立鎖;隨后當線程被阻塞時,它就會一直重復檢查看鎖是否可用了,也就是說當自旋鎖處于等待狀態(tài)時它會一直消耗CPU時間。

    ?

    對于自旋鎖來說,它只需要消耗很少的資源來建立鎖;隨后當線程被阻塞時,它就會一直重復檢查看鎖是否可用了,也就是說當自旋鎖處于等待狀態(tài)時它會一直消耗CPU時間。
    對于互斥鎖來說,與自旋鎖相比它需要消耗大量的系統(tǒng)資源來建立鎖;隨后當線程被阻塞時,線程的調(diào)度狀態(tài)被修改,并且線程被加入等待線程隊列;最后當鎖可用時,在獲取鎖之前,線程會被從等待隊列取出并更改其調(diào)度狀態(tài);但是在線程被阻塞期間,它不消耗CPU資源。

    因此自旋鎖和互斥鎖適用于不同的場景。自旋鎖適用于那些僅需要阻塞很短時間的場景,而互斥鎖適用于那些可能會阻塞很長時間的場景。

    ?

    2.API

    POSIX定義的自旋鎖的數(shù)據(jù)類型是: pthread_spinlock_t
    相關(guān)API?

  • #include?<pthread.h>??
  • int?pthread_spin_init(pthread_spinlock_t?*lock,?int?pshared);成功返回0,其它返回值表示出錯??
  • int?pthread_spin_lock(pthread_spinlock_t?*lock);成功返回0,其它返回值表示出錯??
  • int?pthread_spin_trylock(pthread_spinlock_t?*lock);成功返回0,其它返回值表示出錯??
  • int?pthread_spin_unlock(pthread_spinlock_t?*lock);成功返回0,其它返回值表示出錯??
  • int?pthread_spin_destroy(pthread_spinlock_t?*lock);成功返回0,其它返回值表示出錯??
  • ?

    1)初始化自旋鎖

    pthread_spin_init用來申請使用自旋鎖所需要的資源并且將它初始化為非鎖定狀態(tài)。pshared的取值及其含義:

    • PTHREAD_PROCESS_SHARED:該自旋鎖可以在多個進程中的線程之間共享。
    • PTHREAD_PROCESS_PRIVATE:僅初始化本自旋鎖的線程所在的進程內(nèi)的線程才能夠使用該自旋鎖。

    2)獲得一個自旋鎖

    pthread_spin_lock用來獲取(鎖定)指定的自旋鎖. 如果該自旋鎖當前沒有被其它線程所持有,則調(diào)用該函數(shù)的線程獲得該自旋鎖. 否則該函數(shù)在獲得自旋鎖之前不會返回。如果調(diào)用該函數(shù)的線程在調(diào)用該函數(shù)時已經(jīng)持有了該自旋鎖,則結(jié)果是不確定的。

    3)嘗試獲取一個自旋鎖

    pthread_spin_trylock會嘗試獲取指定的自旋鎖,如果無法獲取則理解返回失敗

    4)釋放(解鎖)一個自旋鎖

    pthread_spin_unlock用于釋放指定的自旋鎖

    5)銷毀一個自旋鎖

    pthread_spin_destroy用來銷毀指定的自旋鎖并釋放所有相關(guān)聯(lián)的資源(所謂的所有指的是由pthread_spin_init自動申請的資源)在調(diào)用該函數(shù)之后如果沒有調(diào)用pthread_spin_init重新初始化自旋鎖,則任何嘗試使用該鎖的調(diào)用的結(jié)果都是未定義的。如果調(diào)用該函數(shù)時自旋鎖正在被使用或者自旋鎖未被初始化則結(jié)果是未定義的。

    ?

    ?

    另外,網(wǎng)上找了蠻久,也沒有找到合適的利用共享內(nèi)存的mutex跨進程共享應(yīng)用,只找到父子進程間的mutex跨進程應(yīng)用。

    可能的確跨進程而言,mutex不如信號量、共享內(nèi)存等來得更方便吧。

    #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h>typedef struct _FOO {int nCount;int nData; }FOO,*PFOO;int main(int argc,char *argv[]) {FOO *ptr;pid_t pid;pthread_mutexattr_t mutexattr;pthread_mutex_t mutex;pthread_mutexattr_init(&mutexattr);pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED); //設(shè)置為進程共享 pthread_mutex_init(&mutex,&mutexattr);ptr = (PFOO)mmap(NULL,sizeof(FOO),PROT_READ | PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0); //匿名內(nèi)存映射,讓父子進程都操作ptr指向的內(nèi)存區(qū),如果不使用共享內(nèi)存,則父子進程的ptr指向的是各自的內(nèi)存空間ptr->nCount = 1;ptr->nData = 2;printf("%d,%d\n",ptr->nCount,ptr->nData);if( (pid = fork()) < 0){printf("fork error\n");return -1;} else if( 0 == pid) //子進程 {for(int i = 0;i<3;i++){pthread_mutex_lock(&mutex);ptr->nCount++;printf("child ++ === %d\n",ptr->nCount);pthread_mutex_unlock(&mutex);usleep(1000);}}else //父進程 {for(int i = 0;i<3;i++){pthread_mutex_lock(&mutex);ptr->nCount += 2;printf("parent +2 === %d\n",ptr->nCount);pthread_mutex_unlock(&mutex);usleep(1000);} }waitpid(pid,NULL,0);munmap(NULL,sizeof(FOO));return 0; }

    ?

    總結(jié)

    以上是生活随笔為你收集整理的【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。