深入 Go 底層原理(二):Channel 的實現剖析

1. 引言

"Do not communicate by sharing memory; instead, share memory by communicating." (不要通過共享內存來通信,而應通過通信來共享內存。) 這是 Go 語言并發設計的核心哲學。而 channel 正是實現這一哲學的核心工具。

Channel 為 Goroutine 之間的通信提供了安全的、同步的機制。它究竟是如何在底層保證并發安全和實現阻塞/非阻塞操作的?本文將深入其源碼,揭示 channel 的內部奧秘。

2. Channel 的核心數據結構

在 Go 的 runtime/chan.go 源碼中,channel 的底層實現是一個名為 hchan 的結構體。其核心字段如下(有簡化):

// src/runtime/chan.go
type hchan struct {qcount   uint           // channel 中當前的元素個數dataqsiz uint           // channel 的容量(環形隊列的大小)buf      unsafe.Pointer // 指向容量大小為 dataqsiz 的環形隊列elemsize uint16         // channel 中元素的大小closed   uint32         // 標記 channel 是否關閉sendx uint // 環形隊列的發送索引recvx uint // 環形隊列的接收索引recvq waitq // 等待接收的 goroutine 隊列 (sudog 鏈表)sendq waitq // 等待發送的 goroutine 隊列 (sudog 鏈表)lock mutex // 保證 channel 操作的原子性
}type waitq struct {first *sudoglast  *sudog
}

核心組件解析

  • buf (環形隊列): 對于帶緩沖的 channel,buf 是一個環形隊列,用于存儲元素。發送和接收操作通過移動 sendxrecvx 索引來完成。

  • lock (互斥鎖): channel 的所有操作(發送、接收、關閉)都必須先獲取這個鎖,這保證了其并發安全性。

  • sendqrecvq (等待隊列): 這是 channel 實現阻塞和喚醒的關鍵。

    • 當一個 goroutine 嘗試向一個已滿的 channel 發送數據時,它會被打包成一個 sudog(goroutine 在運行時的表示)并加入到 sendq 等待隊列中,然后該 goroutine 會被掛起(park)

    • 當一個 goroutine 嘗試從一個空的 channel 接收數據時,它也會被加入到 recvq 等待隊列中并被掛起。

3. Channel 的操作原理
3.1 發送操作 (ch <- data)
  1. 加鎖lock.Lock()

  2. 檢查 closed 標志:如果 channel 已關閉,直接 panic

  3. 檢查 recvq:如果接收等待隊列 recvq 不為空,說明有 goroutine 正在等待接收數據。

    • 這是無緩沖 channel空緩沖 channel的接收者。

    • 直接將要發送的數據拷貝給等待的 goroutine。

    • 喚醒(gounpark)該 goroutine。

    • 解鎖,發送完成。

  4. 檢查 buf:如果 buf (環形隊列) 還有空間 (qcount < dataqsiz)。

    • 將數據拷貝到 bufsendx 位置。

    • sendx 索引遞增。

    • qcount 遞增。

    • 解鎖,發送完成。

  5. 阻塞發送:如果 recvq 為空且 buf 已滿。

    • 將當前 goroutine 和要發送的數據打包成 sudog

    • 加入 sendq 發送等待隊列。

    • 掛起當前 goroutine (gopark),并解鎖。goroutine 會在此等待,直到有接收者將其喚醒。

3.2 接收操作 (<-ch)
  1. 加鎖lock.Lock()

  2. 檢查 sendq:如果發送等待隊列 sendq 不為空。

    • 這通常發生在無緩沖 channel滿緩沖 channel

    • sendq 中取出一個等待的 goroutine。

    • 如果 buf 為空,直接從該 goroutine 中取出數據。

    • 如果 buf 已滿,先將 buf 的隊首元素取出作為返回值,然后將等待 goroutine 的數據存入 buf 隊尾。

    • 喚醒該發送 goroutine。

    • 解鎖,接收完成。

  3. 檢查 buf:如果 buf 中有數據 (qcount > 0)。

    • bufrecvx 位置取出數據。

    • recvx 索引遞增。

    • qcount 遞減。

    • 解鎖,接收完成。

  4. 檢查 closed 標志:如果 channel 已關閉且 buf 為空,立即返回元素類型的零值。

  5. 阻塞接收:如果上述條件都不滿足。

    • 將當前 goroutine 打包成 sudog

    • 加入 recvq 接收等待隊列。

    • 掛起當前 goroutine (gopark) 并解鎖

4. select 的實現

select 語句的實現更為復雜,它會將涉及到的所有 case 構建成一個 scase 數組,然后通過 selectgo 函數執行以下邏輯:

  1. 隨機輪詢:打亂 scase 數組的順序,防止優先級問題。

  2. 非阻塞檢查:遍歷所有 case,檢查是否有任何一個 channel 可以立即進行非阻塞的發送或接收。如果有,則執行該操作并返回。

  3. 阻塞等待:如果所有 case 都無法立即完成,將當前 goroutine 加入到所有相關 channel 的等待隊列中,然后掛起。

  4. 喚醒:當任何一個 channel 的操作條件滿足時(例如,有數據被發送進來),對應的 channel 會喚醒這個等待的 goroutine。goroutine 被喚醒后,會完成相應的 case 操作。

5. 總結

Go channel 的底層是一個由互斥鎖、環形隊列和兩個等待隊列(sudog 鏈表)組成的精密結構。正是通過 lock 保證了并發安全,通過 sendqrecvq 配合調度器的 goparkgounpark,實現了 goroutine 之間的同步與通信。理解這一機制,有助于我們更深刻地運用 Go 的并發能力。

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

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

相關文章

Golang 語言的編程技巧之類型

1、介紹Golang 語言是一門靜態類型的編程語言&#xff0c;我們在編寫代碼時&#xff0c;為了提升代碼的靈活性&#xff0c;有時會使用空接口類型&#xff0c;對于空接口類型的變量&#xff0c;一般會通過類型斷言判斷變量的類型&#xff0c;而且可能還會遇到遇到類型轉換的場景…

計數組合學7.11(RSK算法)

7.11 RSK算法 在對稱函數理論中&#xff0c;有一個非凡的組合對應關系&#xff0c;稱為RSK算法。&#xff08;關于縮寫RSK的含義以及其他名稱&#xff0c;請參閱本章末尾的注釋。&#xff09;這里我們僅介紹RSK算法的最基本性質&#xff0c;從而能夠給出舒爾函數一些基本性質的…

國產嵌入式調試器之光? RT-Trace 初體驗!

做過嵌入式開發的工程師肯定都知道有這么個玩意兒 —— J-Trace&#xff0c;與我們日常使用的普通調試器不同點在于&#xff0c;它在基本的下載/調試代碼之上還具有非常強大的代碼運行跟蹤能力&#xff0c;從而實現代碼覆蓋率的分析、指令回溯、CPU 資源監控等一系列強大的功能…

SLAM中的非線性優化-2D圖優化之零空間實戰(十六)

終于有時間更新實戰篇了&#xff0c;本節實戰幾乎包含了SLAM后端的所有技巧&#xff0c;其中包括&#xff1a;舒爾補/先驗Factor/魯棒核函數/FEJ/BA優化等滑動窗口法的相關技巧&#xff0c;其中構建2D輪式里程計預積分以及絕對位姿觀測的10幀滑動窗口&#xff0c;并邊緣化最老幀…

知識隨記-----Qt 實戰教程:使用 QNetworkAccessManager 發送 HTTP POST

文章目錄Qt 網絡編程&#xff1a;使用 QNetworkAccessManager 實現 HTTP POST 請求概要整體架構流程技術名詞解釋技術細節注意事項&#xff1a;Qt 網絡編程&#xff1a;使用 QNetworkAccessManager 實現 HTTP POST 請求 概要 本文介紹如何使用 Qt 框架的網絡模塊&#xff08;…

wordpress批量新建產品分類

1、下載安裝插件&#xff1a;bulk-category-import-export2、激活插件后&#xff0c;左側點擊插件下的導入&#xff0c;選擇product categories&#xff0c;點擊下一步3、這里可以選擇導入的分類列表文件&#xff0c;可以選擇分隔符&#xff0c;CSV文件默認為‘&#xff0c;’要…

CentOS 鏡像源配置與 EOL 后的應對策略

引言 本文將詳細介紹如何使用 阿里云開源鏡像站 配置 CentOS 的各類軟件源&#xff0c;包括基礎源、歷史歸檔源&#xff08;vault&#xff09;、ARM 架構源、Stream 版本以及調試信息源&#xff08;debuginfo&#xff09;&#xff0c;并重點講解在 CentOS 8 停止維護后&#x…

CTF實戰:用Sqlmap破解表單輸入型SQL注入題(輸入賬號密碼/usernamepassword)

目錄 引言 步驟1&#xff1a;用Burp Suite捕獲表單請求 步驟2&#xff1a;用Sqlmap獲取數據庫名稱 參數解釋&#xff1a; 輸出示例&#xff08;根據題目環境調整&#xff09;&#xff1a; 步驟3&#xff1a;獲取目標數據庫中的表名 參數解釋&#xff1a; 輸出示例&#…

質數時間(二分查找)

題目描述如果把一年之中的某個時間寫作 a 月 b 日 c 時 d 分 e 秒的形式&#xff0c;當這五個數都為質數時&#xff0c;我們把這樣的時間叫做質數時間&#xff0c;現已知起始時刻是 2022 年的 a 月 b 日 c 時 d 分 e 秒&#xff0c;終止時刻是 2022 年的 u 月 v 日 w 時 x 分 y…

Python訓練Day29

浙大疏錦行 類的裝飾器裝飾器思想的進一步理解&#xff1a;外部修改、動態類方法的定義&#xff1a;內部定義和外部定義

新手DBA實戰指南:如何使用gh-ost實現MySQL無鎖表結構變更

新手DBA實戰指南:如何使用gh-ost實現MySQL無鎖表結構變更 作為DBA,大表結構變更(DDL)一直是令人頭疼的問題。傳統的ALTER TABLE操作會鎖表,嚴重影響業務連續性;而常見的pt-online-schema-change工具雖然能實現在線變更,但依賴觸發器機制,在高并發場景下性能表現不佳。本…

OSPF綜合

一、實驗拓撲二、實驗需求1、R4為ISP&#xff0c;其上只配置IP地址&#xff1b;R4與其他所直連設備間均使用公有IP&#xff1b; 2、R3-R5、R6、R7為MGRE環境&#xff0c;R3為中心站點&#xff1b; 3、整個OSPF環境IP基于172.16.0.0/16劃分&#xff1b;除了R12有兩個環回&#x…

技術面試知識點詳解 - 從電路到編程的全棧面經

技術面試知識點詳解 - 從電路到編程的全棧面經 目錄 模擬電路基礎數字電路原理電源設計相關編程語言基礎數據庫與并發網絡協議基礎算法與數據結構 模擬電路基礎 1. 放大電路類型判斷 這是模擬電路面試的經典題目&#xff0c;通過電壓放大倍數判斷放大電路類型&#xff1a; …

LangGraph認知篇-Command函數

Command簡述 在 LangGraph 中&#xff0c;Command 是一個極具實用性的功能&#xff0c;它能夠將控制流&#xff08;邊&#xff09;和狀態更新&#xff08;節點&#xff09;巧妙地結合起來。這意味著開發者可以在同一個節點中&#xff0c;既執行狀態更新操作&#xff0c;又決定下…

【目標檢測】小樣本度量學習

小樣本度量學習&#xff08;Few-Shot Metric Learning&#xff09;通常用于分類任務?&#xff08;如圖像分類&#xff09;&#xff0c;但它也可以與目標檢測&#xff08;Object Detection&#xff09;結合&#xff0c;解決小樣本目標檢測&#xff08;Few-Shot Object Detectio…

cmd怎么取消關機命令

在 Windows 的命令提示符&#xff08;CMD&#xff09;中取消已計劃的關機操作&#xff0c;可以通過 shutdown 命令的 ?**-a**? 參數實現。以下是具體步驟&#xff1a;?操作方法??打開 CMD?按下 Win R 組合鍵&#xff0c;輸入 cmd 并回車&#xff0c;打開命令提示符窗口。…

網易云音樂硬剛騰訊系!起訴SM娛樂濫用市場支配地位

企查查APP顯示&#xff0c;近日&#xff0c;法院公開杭州樂讀科技有限公司、杭州網易云音樂科技有限公司起訴SM ENTERTAINMENT CO. 、卡斯夢&#xff08;上海&#xff09;文化傳播有限公司等開庭信息&#xff0c;案由涉及濫用市場支配地位糾紛。公告顯示&#xff0c;該案件計劃…

[css]切角

使用css實現一個切角的功能&#xff0c;有以下幾種方案&#xff1a; <div class"box"></div>方案一&#xff1a;linear-gradient linear-gradient配合backgroud-image可以實現背景漸變的效果。linear-gradient的漸變過渡區的占比是總的空間&#xff08;高…

分享一個可以測試離線服務器性能的腳本

在日常運維工作中&#xff0c;經常會遇到系統性能莫名跟不上業務需求的情況&#xff1a;服務器響應變慢、應用加載卡頓、資源占用異常飆升等問題頻繁出現&#xff0c;卻難以快速問題根源究竟在CPU過載、內存泄漏、磁盤I/O阻塞還是網絡帶寬瓶頸。這種時候&#xff0c;特別需要一…

Python Pandas.unique函數解析與實戰教程

Python Pandas.unique 函數解析與實戰教程 摘要 本文章旨在全面地解析 pandas 庫中的 unique 函數。pandas.unique 是一個用于從一維數組型(array-like)對象中提取唯一值的高效工具。我們將從其核心功能、函數簽名、參數詳解、返回值類型,到關鍵行為特性(如順序保留、缺失…