【Go】sync.WaitGroup 源碼分析

WaitGroup

sync.WaitGroup 用于等待一組 goroutine 返回,如:

var wg = sync.WaitGroup{}func do() {time.Sleep(time.Second)fmt.Println("done")wg.Done()
}func main() {go do()go do()wg.Add(2)wg.Wait()fmt.Println("main done")
}

概覽

如上面的例子, WaitGroup 只堆外暴露了三個方法:

// 等待的 goroutine 數加 delta
func (wg *WaitGroup) Add(delta int) 
// 等待的 goroutine 數減一
func (wg *WaitGroup) Done() 
// 阻塞,等待這一組 goroutine 全部退出
func (wg *WaitGroup) Wait()
type WaitGroup struct {noCopy noCopystate1 [3]uint32
}

WaitGroup 結構體中也只有兩個字段:

  • noCopy: 用來保證不會被開發者錯誤拷貝
  • state1: 用來保存相關狀態量

另外,他還提供了一個私有的方法用來獲取狀態和信號量

func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]} else {return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]}
}

statep 就是狀態量,注意這里通過 unsafe 將 3 位數組(共 96 位)強轉成了 uint64 這會導致部分數據丟失,具體來說,在64位的機器上會丟失最低 32 位,也即 state1[2] 在 32 位機器上會丟失最高 32 位,也即 state1[0], 這也是 64 位和 32 位機器上數組三位元素表示意義不同的原因。

強轉之后,以 64 位機器為例,數組第二位會作為 statep 的高 32 位,第一位會作為 statep 的低 32 位,也就是說,此時 statep 的結構如下:

+----------------------+-----------------------+
|                      |                       |
|      Counter         |       Waiter          |
|                      |                       |
+----------------------+-----------------------+

Add

func (wg *WaitGroup) Done() {wg.Add(-1)
}

Done 其實就是對 Add 的一個封裝。

func (wg *WaitGroup) Add(delta int) {statep, semap := wg.state()// 把 delta 加到 count 中state := atomic.AddUint64(statep, uint64(delta)<<32)// 獲取 countv := int32(state >> 32)// 丟失高 32 位的 Counter, 得到 Waiterw := uint32(state)if v < 0 {panic("sync: negative WaitGroup counter")}// Waiter 不等于 0 說明現在還有 goroutine 沒有 done, 這時是不允許 Add 的// 也即在 Wait 的過程中不允許通過 Add 添加 if w != 0 && delta > 0 && v == int32(delta) {panic("sync: WaitGroup misuse: Add called concurrently with Wait")}// 正常修改 Counter 后返回if v > 0 || w == 0 {return}// 到這說明 Counter == 0 并且 delta 不是一個正數(執行 Done,并且是最后一次 Done)// 狀態改變,說明有人在 Wait 過程中 Add 了if *statep != state {panic("sync: WaitGroup misuse: Add called concurrently with Wait")}// 狀態置 0*statep = 0// 喚醒 Wait 中的 goroutinefor ; w != 0; w-- {runtime_Semrelease(semap, false, 0)}
}

總結一下,首先 Done 只是對 Add 的簡單封裝,在 Add 時,通過巧妙利用精度丟失和位移運算分別計算出 add 后的 Counter 和 Waiter, 前者表示已經 add 了多少 Goroutine, 后者表示還有多少個 goroutine 需要 Wait, 這里需要注意,在 Wait 的過程中是不允許 Add 新 goroutine 的;在執行 Done 時,只是簡單的將 Counter 減 1,直到 Counter == 1 時,也即最后一個 goroutine 已經執行完畢時,Done 會通知 Wait 停止阻塞,并將標志清空。

Wait

func (wg *WaitGroup) Wait() {statep, semap := wg.state()for {state := atomic.LoadUint64(statep)v := int32(state >> 32)// Counter == 0, 沒有 Add, 直接返回if v == 0 {return}// 每一次 CAS 讓 Waiter 加一,并進入阻塞,等待最后一個 Done 的 goroutine 將其喚醒if atomic.CompareAndSwapUint64(statep, state, state+1) {runtime_Semacquire(semap)if *statep != 0 {panic("sync: WaitGroup is reused before previous Wait has returned")}return}// 如果 CAS 比較沒通過,說明在此過程中有 goroutine Done 了,需要重新去獲取最新的狀態}
}

總結

WaitGroup 用于阻塞某個 Goroutine 以等待一組 goroutine 返回,在實現上,它采用一個長度為 3 的 32 位無符號整型數組保存 Waiter, Counter, 和信號量,每次 Add 時,會將 Counder 加上 delta,而當執行 Done 或 delta 為負數時,如果 Done 的是最后一個 Goroutine, Add 會去喚醒 Wait

執行 Wait 只是將 Waiter 加一并阻塞等待 Add 的喚醒,所以其實 Waiter 的值只會是 0 或 1.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457411.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457411.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457411.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

什么是響應式設計?為什么要做響應式設計?響應式設計的基本原理是什么?...

頁面的設計和開發應當根據用戶行為以及設備環境&#xff08;系統平臺、屏幕尺寸、屏幕定向等&#xff09;進行相應的響應和調整。具體的實踐方式由多方面組成&#xff0c;包括彈性網格和布局、圖片、css media query的使用等。無論用戶正在使用筆記本還是iPad&#xff0c;我們的…

三個數相減的平方公式_快收好這份小學數學公式大全!孩子遇到數學難題時肯定用得上...

必背定義、定理公式1.三角形的面積&#xff1d;底高2 公式 S&#xff1d; ah22.正方形的面積&#xff1d;邊長邊長公式 S&#xff1d; aa3.長方形的面積&#xff1d;長寬公式 S&#xff1d; ab4.平行四邊形的面積&#xff1d;底高公式 S&#xff1d; ah5.梯形的面積&#xff1d…

Eclipse 控制console

http://blog.csdn.net/leidengyan/article/details/5686691

【Go】sync.RWMutex源碼分析

RWMutex 讀寫鎖相較于互斥鎖有更低的粒度&#xff0c;它允許并發讀&#xff0c;因此在讀操作明顯多于寫操作的場景下能減少鎖競爭的次數&#xff0c;提高程序效率。 type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // sem…

add.attribute向前端傳_前端知識-概念篇

1、一次完整的HTTP事務是怎樣的一個過程&#xff1f;基本流程&#xff1a;a. 域名解析b. 發起TCP的3次握手c. 建立TCP連接后發起http請求d. 服務器端響應http請求&#xff0c;瀏覽器得到html代碼e. 瀏覽器解析html代碼&#xff0c;并請求html代碼中的資源f. 瀏覽器對頁面進行渲…

【數據庫】一篇文章搞懂數據庫隔離級別那些事(LBCC,MVCC)

MySQL 事務 文章比較長&#xff0c;建議分段閱讀 后續如果有改動會在 Junebao.top 之前對事務的了解僅限于知道要么全部執行&#xff0c;要么全部不執行&#xff0c;能背出 ACID 和隔離級別&#xff0c;知其然但不知其所以然&#xff0c;現在覺得非常有必要系統學一下&#xff…

AFNetworking網絡請求與圖片上傳工具(POST)

AFNetworking網絡請求與圖片上傳工具&#xff08;POST&#xff09; .h文件 #import <Foundation/Foundation.h>/** 成功Block */ typedef void(^SuccessBlockType) (id responsData); /** 失敗Block */ typedef void(^FaileBlockType) (NSError *error);interface NetD…

api商品分享源碼_SSM框架高并發和商品秒殺項目高并發秒殺API源碼免費分享

前言&#xff1a;一個整合SSM框架的高并發和商品秒殺項目,學習目前較流行的Java框架組合實現高并發秒殺API源碼獲取&#xff1a;關注頭條號轉發文章之后私信【秒殺】查看源碼獲取方式&#xff01;項目的來源項目的來源于國內IT公開課平臺,質量沒的說,很適合學習一些技術的基礎,…

Golang 定時任務 github/robfig/cron/v3 使用與源碼解析

Cron 源碼閱讀 robfig/cron/v3 是一個 Golang 的定時任務庫&#xff0c;支持 cron 表達式。Cron 的源碼真實教科書級別的存在&#xff08;可能是我菜 …&#xff09;,真的把低耦合高內聚體現地淋漓盡致&#xff0c;另外其中涉及的裝飾器模式&#xff0c;并發處理等都很值得學習…

修改 cmd 字體為 Consolas

windows 下的 cmd 窗口默認的字體有點難看&#xff0c;長時間使用操作 node.js 有點小疲勞&#xff0c;可以修改注冊表替換字體為 Consolas&#xff0c;并且可以全屏 cmd 窗口&#xff0c;代碼如下&#xff1a; Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Conso…

mac下安裝前端模板引擎Jinja2

在mac本上安裝Jinja2&#xff0c;搜索網上介紹的經驗&#xff0c;都是說使用easy_install或者pip安裝&#xff0c;比如 #sudo easy_install Jinja2 #sudo pip install Jinja2 也有直接使用 #easy_install Jinja2的&#xff0c;但是我使用上述命令安裝總是不成功&#xff0c;提示…

為什么要用python不用origin_Python告訴你為什么百度已死

Python3爬蟲百度一下&#xff0c;坑死你&#xff1f;一、寫在前面這個標題是借用的路人甲大佬的一篇文章的標題(百度一下&#xff0c;坑死你)&#xff0c;而且這次的爬蟲也是看了這篇文章后才寫出來的&#xff0c;感興趣的可以先看下這篇文章。前段時間有篇文章《搜索引擎百度已…

關于 HTTP 的一切(HTTP/1.1,HTTP/2,HTTP/3,HTTPS, CORS, 緩存 ,無狀態)

HTTP 為什么會出現 HTTP 協議&#xff0c;從 HTTP1.0 到 HTTP3 經歷了什么&#xff1f;HTTPS 又是怎么回事&#xff1f; HTTP 是一種用于獲取類似于 HTML 這樣的資源的 應用層通信協議&#xff0c; 他是萬維網的基礎&#xff0c;是一種 CS 架構的協議&#xff0c;通常來說&…

AS 2.0新功能 Instant Run

Instant Run上手作為一個Android開發者&#xff0c;很多的時候我們需要花大量的時間在bulid&#xff0c;運行到真機&#xff08;虛擬機&#xff09;上&#xff0c;對于ios上的Playground羨慕不已&#xff0c;這種情況將在Android Studio 2.0有了很大改善&#xff0c;使用instan…

爬蟲cookie過期_python instagram 爬蟲

葉湘倫&#xff1a;【文字篇】如何系統地自學 Python&#xff1f;?zhuanlan.zhihu.com直接介紹一下具體的步驟以及注意點&#xff1a;instagram 爬蟲注意點instagram 的首頁數據是 服務端渲染的&#xff0c;所以首頁出現的 11 或 12 條數據是以 html 中的一個 json 結構存在的…

php 無限循環

<?php header("Content-type:text/html;charsetutf-8"); $arr array( array(1, 0, 語文), array(2, 1, 數學), array(3, 0, 英文), array(4, 3, 美術), ); function xunhuan($pid 0) { global $arr; foreach ($arr as $value) { if ($value[1] $pid) { ech…

MySQL InnoDB 是如何存儲數據的

InnoDB 是怎么存儲數據的 本文是《MySQL 是怎樣運行的 —— 從根兒上理解 MySQL》讀書總結&#xff0c;強烈推薦這本書&#xff1b; CSDN 不能顯示 SVG&#xff0c;可能有圖片加載不出來&#xff0c;可以到 我的博客 上看。 數據目錄 眾所周之&#xff0c;MySQL 的數據是存儲在…

蔬菜大棚成本_蔬菜大棚種植成本和利潤究竟如何?種植戶有話說

大棚蔬菜種植&#xff0c;到底利潤高不高&#xff0c;就讓親身體驗過的人來說下自己的情況吧。農大老家山東也是大棚蔬菜種植比較早的地方&#xff0c;直到現在大棚種植蔬菜在各地都還是不少。大棚蔬菜種植&#xff0c;是有相應的補貼政策&#xff0c;在農業種植當中&#xff0…

WebSocket實戰之————GatewayWorker使用筆記例子

參考文檔&#xff1a;http://www.workerman.net/gatewaydoc/ 目錄結構 ├── Applications // 這里是所有開發者應用項目 │ └── YourApp // 其中一個項目目錄&#xff0c;目錄名可以自定義 │ ├── Events.php // 開發者只需要關注這個文件 │ ├── st…

[轉]關于凸優化的一些簡單概念

沒有系統學過數學優化&#xff0c;但是機器學習中又常用到這些工具和技巧&#xff0c;機器學習中最常見的優化當屬凸優化了&#xff0c;這些可以參考Ng的教學資料&#xff1a;http://cs229.stanford.edu/section/cs229-cvxopt.pdf&#xff0c;從中我們可以大致了解到一些凸優化…