<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 1 Mutex數據結構 ### 1.1 Mutex結構體 源碼包`src/sync/mutex.go:Mutex`定義了互斥鎖的數據結構: ~~~go type Mutex struct { state int32 sema uint32 } ~~~ * Mutex.state表示互斥鎖的狀態,比如是否被鎖定等。 * Mutex.sema表示信號量,協程阻塞等待該信號量,解鎖的協程釋放信號量從而喚醒等待信號量的協程。 我們看到Mutex.state是32位的整型變量,內部實現時把該變量分成四份,用于記錄Mutex的四種狀態。 下圖展示Mutex的內存布局: ![](https://img.kancloud.cn/62/53/62537e743f76c0f256c36eea0d89841c_735x237.png) * Locked: 表示該Mutex是否已被鎖定,0:沒有鎖定 1:已被鎖定。 * Woken: 表示是否有協程已被喚醒,0:沒有協程喚醒 1:已有協程喚醒,正在加鎖過程中。 * Starving:表示該Mutex是否處于饑餓狀態,0:沒有饑餓 1:饑餓狀態,說明有協程阻塞了超過1ms。 * Waiter: 表示阻塞等待鎖的協程個數,協程解鎖時根據此值來判斷是否需要釋放信號量。 協程之間搶鎖實際上是搶給Locked賦值的權利,能給Locked域置1,就說明搶鎖成功。搶不到的話就阻塞等待Mutex.sema信號量,一旦持有鎖的協程解鎖,等待的協程會依次被喚醒。 Woken和Starving主要用于控制協程間的搶鎖過程,后面再進行了解。 ### 1.2 Mutex方法 Mutex對外提供兩個方法,實際上也只有這兩個方法: * Lock() : 加鎖方法 * Unlock(): 解鎖方法 下面我們分析一下加鎖和解鎖的過程,加鎖分成功和失敗兩種情況,成功的話直接獲取鎖,失敗后當前協程被阻塞,同樣,解鎖時根據是否有阻塞協程也有兩種處理。 ## 3 加解鎖過程 ## 3.1 簡單加鎖 假定當前只有一個協程在加鎖,沒有其他協程干擾,那么過程如下圖所示: ![](https://img.kancloud.cn/b0/29/b02992c18b74569eef78f4de76e5e09e_841x307.png) 加鎖過程會去判斷Locked標志位是否為0,如果是0則把Locked位置1,代表加鎖成功。從上圖可見,加鎖成功后,只是Locked位置1,其他狀態位沒發生變化 ## 3.2 加鎖被阻塞 假定加鎖時,鎖已被其他協程占用了,此時加鎖過程如下圖所示: ![](https://img.kancloud.cn/32/d3/32d300a03fc3b58b0f4552d6bd3d0938_841x457.png) 從上圖可看到,當協程B對一個已被占用的鎖再次加鎖時,Waiter計數器增加了1,此時協程B將被阻塞,直到Locked值變為0后才會被喚醒。 ## 3.3 簡單解鎖 假定解鎖時,沒有其他協程阻塞,此時解鎖過程如下圖所示: ![](https://img.kancloud.cn/91/72/91725a70081dffd1091bd94bca494ee0_841x457.png) 由于沒有其他協程阻塞等待加鎖,所以此時解鎖時只需要把Locked位置為0即可,不需要釋放信號量 ## 3.4 解鎖并喚醒協程 假定解鎖時,有1個或多個協程阻塞,此時解鎖過程如下圖所示: ![](https://img.kancloud.cn/59/b9/59b900033000d67da9ed3c071143d897_841x737.png) 協程A解鎖過程分為兩個步驟,一是把Locked位置0,二是查看到Waiter>0,所以釋放一個信號量,喚醒一個阻塞的協程,被喚醒的協程B把Locked位置1,于是協程B獲得鎖。 ## 4 自旋過程 加鎖時,如果當前Locked位為1,說明該鎖當前由其他協程持有,嘗試加鎖的協程并不是馬上轉入阻塞,而是會持續的探測Locked位是否變為0,這個過程即為自旋過程。 自旋時間很短,但如果在自旋過程中發現鎖已被釋放,那么協程可以立即獲取鎖。此時即便有協程被喚醒也無法獲取鎖,只能再次阻塞。 自旋的好處是,當加鎖失敗時不必立即轉入阻塞,有一定機會獲取到鎖,這樣可以避免協程的切換。 ## 4.1 什么是自旋 自旋對應于CPU的”PAUSE”指令,CPU對該指令什么都不做,相當于CPU空轉,對程序而言相當于sleep了一小段時間,時間非常短,當前實現是30個時鐘周期。 自旋過程中會持續探測Locked是否變為0,連續兩次探測間隔就是執行這些PAUSE指令,它不同于sleep,不需要將協程轉為睡眠狀態。 ## 4.2 自旋條件 加鎖時程序會自動判斷是否可以自旋,無限制的自旋將會給CPU帶來巨大壓力,所以判斷是否可以自旋就很重要了。 自旋必須滿足以下所有條件: * 自旋次數要足夠小,通常為4,即自旋最多4次 * CPU核數要大于1,否則自旋沒有意義,因為此時不可能有其他協程釋放鎖 * 協程調度機制中的Process數量要大于1,比如使用GOMAXPROCS()將處理器設置為1就不能啟用自旋 * 協程調度機制中的可運行隊列必須為空,否則會延遲協程調度 可見,自旋的條件是很苛刻的,總而言之就是不忙的時候才會啟用自旋。 ## 4.3 自旋的優勢 自旋的優勢是更充分的利用CPU,盡量避免協程切換。因為當前申請加鎖的協程擁有CPU,如果經過短時間的自旋可以獲得鎖,當前協程可以繼續運行,不必進入阻塞狀態。 ## 4.4 自旋的問題 如果自旋過程中獲得鎖,那么之前被阻塞的協程將無法獲得鎖,如果加鎖的協程特別多,每次都通過自旋獲得鎖,那么之前被阻塞的進程將很難獲得鎖,從而進入饑餓狀態。 為了避免協程長時間無法獲取鎖,自1.8版本以來增加了一個狀態,即Mutex的Starving狀態。這個狀態下不會自旋,一旦有協程釋放鎖,那么一定會喚醒一個協程并成功加鎖。 ## 5 Woken狀態 Woken狀態用于加鎖和解鎖過程的通信,舉個例子,同一時刻,兩個協程一個在加鎖,一個在解鎖,在加鎖的協程可能在自旋過程中,此時把Woken標記為1,用于通知解鎖協程不必釋放信號量了,好比在說:你只管解鎖好了,不必釋放信號量,我馬上就拿到鎖了。 ## 6為什么重復解鎖要panic 可能你會想,為什么Go不能實現得更健壯些,多次執行Unlock()也不要panic? 仔細想想Unlock的邏輯就可以理解,這實際上很難做到。Unlock過程分為將Locked置為0,然后判斷Waiter值,如果值>0,則釋放信號量。 如果多次Unlock(),那么可能每次都釋放一個信號量,這樣會喚醒多個協程,多個協程喚醒后會繼續在Lock()的邏輯里搶鎖,勢必會增加Lock()實現的復雜度,也會引起不必要的協程切換。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看