隨筆-204  評論-149  文章-0  trackbacks-0

          很顯然,pthread中的條件變量與Java中的wait,notify類似

          假設有共享的資源sum,與之相關聯的mutex 是lock_s.假設每個線程對sum的操作很簡單的,與sum的狀態無關,比如只是sum++.那么只用mutex足夠了.程序員只要確保每個線程操作前,取得lock,然后sum++,再unlock即可.每個線程的代碼將像這樣add()
          {
            pthread_mutex_lock(lock_s);
            sum++;
            pthread_mutex_unlock(lock_s);
          }

            如果操作比較復雜,假設線程t0,t1,t2的操作是sum++,而線程t3則是在sum到達100的時候,打印出一條信息,并對sum清零.這種情況下,如果只用mutex, 則t3需要一個循環,每個循環里先取得lock_s,然后檢查sum的狀態,如果sum>=100,則打印并清零,然后unlock.如果sum<100,則unlock,并sleep()本線程合適的一段時間.

           這個時候,t0,t1,t2的代碼不變,t3的代碼如下
            print()
            {
              while (1)
              {
                pthread_mutex_lock(lock_s);
                if(sum<100)
                {
                  printf(“sum reach 100!”);
                  pthread_mutex_unlock(lock_s);
                }
                else
                {
                  pthread_mutex_unlock(lock_s);
                  my_thread_sleep(100);
                  return OK;
                }
              }
            }

          這種辦法有兩個問題
            1) sum在大多數情況下不會到達100,那么對t3的代碼來說,大多數情況下,走的是else分支,只是lock和unlock,然后sleep().這浪費了CPU處理時間.
            2) 為了節省CPU處理時間,t3會在探測到sum沒到達100的時候sleep()一段時間.這樣卻又帶來另外一個問題,亦即t3響應速度下降.可能在sum到達200的時候,t3才會醒過來.
            3) 這樣,程序員在設置sleep()時間的時候陷入兩難境地,設置得太短了節省不了資源,太長了又降低響應速度.真是難辦啊!

            這個時候,condition variable內褲外穿,從天而降,拯救了焦頭爛額的你.

            你首先定義一個condition variable.
            pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER;

            t0,t1,t2的代碼只要后面加兩行,像這樣
            add()
            {
              pthread_mutex_lock(lock_s);
              sum++;
              pthread_mutex_unlock(lock_s);
              if(sum>=100)
              pthread_cond_signal(&cond_sum_ready);
            }
            而t3的代碼則是
            print
            {
              pthread_mutex_lock(lock_s);
              while(sum<100)
              pthread_cond_wait(&cond_sum_ready, &lock_s);
              printf(“sum is over 100!”);
              sum=0;
              pthread_mutex_unlock(lock_s);
              return OK;
            }

          注意兩點:
            1) 在thread_cond_wait()之前,必須先lock相關聯的mutex, 因為假如目標條件未滿足,pthread_cond_wait()實際上會unlock該mutex, 然后block,在目標條件滿足后再重新lock該mutex, 然后返回.

            2) 為什么是while(sum<100),而不是if(sum<100) ?這是因為在pthread_cond_signal()和pthread_cond_wait()返回之間,有時間差,假設在這個時間差內,還有另外一個線程t4又把sum減少到100以下了,那么t3在pthread_cond_wait()返回之后,顯然應該再檢查一遍sum的大小.這就是用while的用意


          boost lib

          http://www.stlchina.org/twiki/bin/view.pl/Main/BoostThread#safag7ZYn7rqw


          文章出處:DIY部落(http://www.diybl.com/course/3_program/c++/cppxl/20090508/166837.html)

          posted on 2009-07-05 01:07 Frank_Fang 閱讀(9243) 評論(2)  編輯  收藏 所屬分類: Linux | ACE網絡編程

          評論:
          # re: 線程同步:何時互斥鎖不夠,還需要條件變量? 2009-07-08 11:25 | Frank_Fang

          援引CU上一篇帖子的內容:
          “信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作(大家都在semtake的時候,就阻塞在 哪里)。而互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那么別的線程就無法訪問,直到這個線程unlock,其他的線程才開始可以利用這 個資源。比如對全局變量的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和信號量會同時使用的”
          也就是說,信號量不一定是鎖定某一個資源,而是流程上的概念,比如:有A,B兩個線程,B線程要等A線程完成某一任務以后再進行自己下面的步驟,這個任務 并不一定是鎖定某一資源,還可以是進行一些計算或者數據處理之類。而線程互斥量則是“鎖住某一資源”的概念,在鎖定期間內,其他線程無法對被保護的數據進 行操作。在有些情況下兩者可以互換。

          兩者之間的區別:

          作用域
          信號量: 進程間或線程間(linux僅線程間的無名信號量pthread semaphore)
          互斥鎖: 線程間

          上鎖時
          信號量: 只要信號量的value大于0,其他線程就可以sem_wait成功,成功后信號量的value減一。若value值不大于0,則sem_wait使得線程阻塞,直到sem_post釋放后value值加一,但是sem_wait返回之前還是會將此value值減一
          互斥鎖: 只要被鎖住,其他任何線程都不可以訪問被保護的資源

          以下是信號燈(量)的一些概念:

          信號燈與互斥鎖和條件變量的主要不同在于”燈”的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說后兩中同步方式側重于”等待”操作,即資 源不可用的話,信號燈機制則側重于點燈,即告知資源可用;
          沒有等待線程的解鎖或激發條件都是沒有意義的,而沒有等待燈亮的線程的點燈操作則有效,且能保持 燈亮狀態。當然,這樣的操作原語也意味著更多的開銷。

          信號燈的應用除了燈亮/燈滅這種二元燈以外,也可以采用大于1的燈數,以表示資源數大于1,這時可以稱之為多元燈。

          1. 創建和 注銷

          POSIX信號燈標準定義了有名信號燈和無名信號燈兩種,但LinuxThreads的實現僅有無名燈,同時有名燈除了總是可用于多進程之間以外,在使用上與無名燈并沒有很大的區別,因此下面僅就無名燈進行討論。

          int sem_init(sem_t *sem, int pshared, unsigned int value)
          這是創建信號燈的API,其中value為信號燈的初值,pshared表示是否為多進程共享而不僅僅是用于一個進程。LinuxThreads沒有實現 多進程共享信號燈,因此所有非0值的pshared輸入都將使sem_init()返回-1,且置errno為ENOSYS。初始化好的信號燈由sem變 量表征,用于以下點燈、滅燈操作。

          int sem_destroy(sem_t * sem)
          被注銷的信號燈sem要求已沒有線程在等待該信號燈,否則返回-1,且置errno為EBUSY。除此之外,LinuxThreads的信號燈 注銷函數不做其他動作。
          sem_destroy destroys a semaphore object, freeing the resources it  might  hold.  No  threads  should  be  waiting  on  the
                 semaphore  at  the  time  sem_destroy  is  called.  In  the  LinuxThreads implementation, no resources are associated with
                 semaphore objects, thus sem_destroy actually does nothing except checking that no thread is waiting on the semaphore.


          2. 點燈和滅燈

          int sem_post(sem_t * sem)

          點燈操作將信號燈值原子地加1,表示增加一個可訪問的資源。

          int sem_wait(sem_t * sem)
          int sem_trywait(sem_t * sem)

          sem_wait()為等待燈亮操作,等待燈亮(信號燈值大于0),然后將信號燈原子地減1,并返回。sem_trywait()為sem_wait()的非阻塞版,如果信號燈計數大于0,則原子地減1并返回0,否則立即返回-1,errno置為EAGAIN。

          3. 獲取燈值

          int sem_getvalue(sem_t * sem, int * sval)

          讀取sem中的燈計數,存于*sval中,并返回0。

          4. 其他

          sem_wait()被實現為取消點。取消點事什么意思???)
          sem_wait is a cancellation point.
          取消點的含義:
          當用pthread_cancel()一個線程時,這個要求會被pending起來,當被cancel的線程走到下一個cancellation point時,線程才會被真正cancel掉。

          而且在支持原子”比較且交換CAS”指令的體系結構上,sem_post()是唯一能用于異步信號處理函數的POSIX異步信號 安全的API。

          On processors supporting atomic compare-and-swap (Intel 486, Pentium and later, Alpha, PowerPC, MIPS  II,  Motorola  68k),
                 the  sem_post function is async-signal safe and can therefore be called from signal handlers. This is the only thread syn-
                 chronization function provided by POSIX threads that is async-signal safe.

                 On the Intel 386 and the Sparc, the current LinuxThreads implementation of sem_post is not async-signal safe  by  lack  of
                 the required atomic operations.

            回復  更多評論
            
          # re: 線程同步:何時互斥鎖不夠,還需要條件變量? 2009-07-08 14:08 | Frank_Fang

          互斥量(Mutex)

           

          互斥量表現互斥現象的數據結構,也被當作二元信號燈。一個互斥基本上是一個多任務敏感的二元信號,它能用作同步多任務的行為,它常用作保護從中斷來的臨界段代碼并且在共享同步使用的資源。

          clip_image001

           

          Mutex本質上說就是一把鎖,提供對資源的獨占訪問,所以Mutex主要的作用是用于互斥。Mutex對象的值,只有0和1兩個值。這兩個值也分別代表了Mutex的兩種狀態。值為0, 表示鎖定狀態,當前對象被鎖定,用戶進程/線程如果試圖Lock臨界資源,則進入排隊等待;值為1,表示空閑狀態,當前對象為空閑,用戶進程/線程可以Lock臨界資源,之后Mutex值減1變為0。

          Mutex可以被抽象為四個操作:

          - 創建 Create

          - 加鎖 Lock

          - 解鎖 Unlock

          - 銷毀 Destroy

          Mutex被創建時可以有初始值,表示Mutex被創建后,是鎖定狀態還是空閑狀態。在同一個線程中,為了防止死鎖,系統不允許連續兩次對Mutex加鎖(系統一般會在第二次調用立刻返回)。也就是說,加鎖和解鎖這兩個對應的操作,需要在同一個線程中完成。

          不同操作系統中提供的Mutex函數:

          動作\系統

          Win32

          Linyx

          Solaris

          創建

          CreateMutex

          pthread_mutex_init

          mutex_init

          加鎖

          WaitForSingleObject

          pthread_mutex_lock

          mutex_lock

          解鎖

          ReleaseMutex

          pthread_mutex_unlock

          mutex_unlock

          銷毀

          CloseHandle

          pthread_mutex_destroy

          mutex_destroy

           

          信號量

          信號量(Semaphore),有時被稱為信號燈,是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們能夠正確、合理的使用公共資源。

          信號量可以分為幾類:

          ² 二進制信號量(binary semaphore):只允許信號量取0或1值,其同時只能被一個線程獲取。

          ² 整型信號量(integer semaphore):信號量取值是整數,它可以被多個線程同時獲得,直到信號量的值變為0。

          ² 記錄型信號量(record semaphore):每個信號量s除一個整數值value(計數)外,還有一個等待隊列List,其中是阻塞在該信號量的各個線程的標識。當信號量被釋放一個,值被加一后,系統自動從等待隊列中喚醒一個等待中的線程,讓其獲得信號量,同時信號量再減一。

          信號量通過一個計數器控制對共享資源的訪問,信號量的值是一個非負整數,所有通過它的線程都會將該整數減一。如果計數器大于0,則訪問被允許,計數器減1;如果為0,則訪問被禁止,所有試圖通過它的線程都將處于等待狀態。

          計數器計算的結果是允許訪問共享資源的通行證。因此,為了訪問共享資源,線程必須從信號量得到通行證, 如果該信號量的計數大于0,則此線程獲得一個通行證,這將導致信號量的計數遞減,否則,此線程將阻塞直到獲得一個通行證為止。當此線程不再需要訪問共享資源時,它釋放該通行證,這導致信號量的計數遞增,如果另一個線程等待通行證,則那個線程將在那時獲得通行證。

           

          Semaphore可以被抽象為五個操作:

          - 創建 Create

          - 等待 Wait:

          線程等待信號量,如果值大于0,則獲得,值減一;如果只等于0,則一直線程進入睡眠狀態,知道信號量值大于0或者超時。

          -釋放 Post

          執行釋放信號量,則值加一;如果此時有正在等待的線程,則喚醒該線程。

          -試圖等待 TryWait

          如果調用TryWait,線程并不真正的去獲得信號量,還是檢查信號量是否能夠被獲得,如果信號量值大于0,則TryWait返回成功;否則返回失敗。

          -銷毀 Destroy

          信號量,是可以用來保護兩個或多個關鍵代碼段,這些關鍵代碼段不能并發調用。在進入一個關鍵代碼段之前,線程必須獲取一個信號量。如果關鍵代碼段中沒有任何線程,那么線程會立即進入該框圖中的那個部分。一旦該關鍵代碼段完成了,那么該線程必須釋放信號量。其它想進入該關鍵代碼段的線程必須等待直到第一個線程釋放信號量。為了完成這個過程,需要創建一個信號量,然后將Acquire Semaphore VI以及Release Semaphore VI分別放置在每個關鍵代碼段的首末端。確認這些信號量VI引用的是初始創建的信號量。

          動作\系統

          Win32

          POSIX

          創建

          CreateSemaphore

          sem_init

          等待

          WaitForSingleObject

          sem _wait

          釋放

          ReleaseMutex

          sem _post

          試圖等待

          WaitForSingleObject

          sem _trywait

          銷毀

          CloseHandle

          sem_destroy

          互斥量和信號量的區別

          1. 互斥量用于線程的互斥,信號量用于線程的同步。

          這是互斥量和信號量的根本區別,也就是互斥和同步之間的區別。

          互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。

          同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源

          2. 互斥量值只能為0/1,信號量值可以為非負整數。

          也就是說,一個互斥量只能用于一個資源的互斥訪問,它不能實現多個資源的多線程互斥問題。信號量可以實現多個同類資源的多線程互斥和同步。當信號量為單值信號量是,也可以完成一個資源的互斥訪問。

          3. 互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到。

            回復  更多評論
            
          主站蜘蛛池模板: 兴文县| 永清县| 勃利县| 钟山县| 揭阳市| 鹤峰县| 呈贡县| 诏安县| 定远县| 景德镇市| 当雄县| 徐闻县| 北安市| 浑源县| 台北市| 资中县| 阳西县| 万全县| 湘乡市| 河津市| 巧家县| 额敏县| 沙坪坝区| 桐庐县| 合水县| 凤台县| 灵武市| 浦江县| 永康市| 阿荣旗| 阜新市| 保康县| 衡山县| 柳林县| 哈巴河县| 孟州市| 高邮市| 云阳县| 阜宁县| 怀远县| 焉耆|