go Channel原理 (三)

Channel

設計原理

不要通過共享內存的方式進行通信,而是應該通過通信的方式共享內存。

在主流編程語言中,多個線程傳遞數據的方式一般都是共享內存。
在這里插入圖片描述
Go 可以使用共享內存加互斥鎖進行通信,同時也提供了一種不同的并發模型,即通信順序進程(Communicating sequential processes,CSP)。Goroutine 和 Channel 分別對應 CSP 中的實體和傳遞信息的媒介,Goroutine 之間會通過 Channel 傳遞數據。在這里插入圖片描述
上圖中的兩個 Goroutine,一個會向 Channel 中發送數據,另一個會從 Channel 中接收數據,它們兩者能夠獨立運行并不存在直接關聯,但是能通過 Channel 間接完成通信。

接收數據

兩個 Goroutine,一個會向 Channel 中發送數據,另一個會從 Channel 中接收數據,它們兩者能夠獨立運行并不存在直接關聯,但是能通過 Channel 間接完成通信。這是一個 生產者 - 消費者 模型,負責接收數據的 goroutine 從 channel 讀取一個消息進行消費,channel 起到一個臨界區/緩沖區的作用。

// chanrecv 函數接收 channel c 的元素并將其寫入 ep 所指向的內存地址。
// 如果 ep 是 nil,說明忽略了接收值。
// 如果 block == false,即非阻塞型接收,在沒有數據可接收的情況下,返回 (false, false)
// 否則,如果 c 處于關閉狀態,將 ep 指向的地址清零,返回 (true, false)
// 否則,用返回值填充 ep 指向的內存地址。返回 (true, true)
// 如果 ep 非空,則應該指向堆或者函數調用者的棧func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {// 省略 debug 內容 …………// 如果是一個 nil 的 channelif c == nil {// 如果不阻塞,直接返回 (false, false)if !block {return}// 否則,接收一個 nil 的 channel,goroutine 掛起gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2)// 不會執行到這里throw("unreachable")}// 在非阻塞模式下,快速檢測到失敗,不用獲取鎖,快速返回// 當我們觀察到 channel 沒準備好接收:// 1. 非緩沖型,等待發送列隊 sendq 里沒有 goroutine 在等待// 2. 緩沖型,但 buf 里沒有元素// 之后,又觀察到 closed == 0,即 channel 未關閉。// 因為 channel 不可能被重復打開,所以前一個觀測的時候 channel 也是未關閉的,// 因此在這種情況下可以直接宣布接收失敗,返回 (false, false)if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0) &&atomic.Load(&c.closed) == 0 {return}var t0 int64if blockprofilerate > 0 {t0 = cputicks()}// 加鎖lock(&c.lock)// channel 已關閉,并且循環數組 buf 里沒有元素// 這里可以處理非緩沖型關閉 和 緩沖型關閉但 buf 無元素的情況// 也就是說即使是關閉狀態,但在緩沖型的 channel,// buf 里有元素的情況下還能接收到元素if c.closed != 0 && c.qcount == 0 {if raceenabled {raceacquire(unsafe.Pointer(c))}// 解鎖unlock(&c.lock)if ep != nil {// 從一個已關閉的 channel 執行接收操作,且未忽略返回值// 那么接收的值將是一個該類型的零值// typedmemclr 根據類型清理相應地址的內存typedmemclr(c.elemtype, ep)}// 從一個已關閉的 channel 接收,selected 會返回truereturn true, false}// 等待發送隊列里有 goroutine 存在,說明 buf 是滿的// 1. 非緩沖型的 channel。直接進行內存拷貝(從 sender goroutine -> receiver goroutine)// 2. 緩沖型的 channel,但 buf 滿了。接收到循環數組頭部的元素,并將發送者的元素放到循環數組尾部if sg := c.sendq.dequeue(); sg != nil {// Found a waiting sender. If buffer is size 0, receive value// directly from sender. Otherwise, receive from head of queue// and add sender's value to the tail of the queue (both map to// the same buffer slot because the queue is full).recv(c, sg, ep, func() { unlock(&c.lock) }, 3)return true, true}// 緩沖型,buf 里有元素,可以正常接收if c.qcount > 0 {// 直接從循環數組里找到要接收的元素qp := chanbuf(c, c.recvx)// …………// 沒有忽略要接收的值,不是 "<- ch",而是 "val <- ch",ep 指向 valif ep != nil {typedmemmove(c.elemtype, ep, qp)}// 清理掉循環數組里相應位置的值typedmemclr(c.elemtype, qp)// 接收游標向前移動c.recvx++// 接收游標歸零if c.recvx == c.dataqsiz {c.recvx = 0}// buf 數組里的元素個數減 1c.qcount--// 解鎖unlock(&c.lock)return true, true}if !block {// 非阻塞接收,解鎖。selected 返回 false,因為沒有接收到值unlock(&c.lock)return false, false}// 構造一個 sudog 并設置相應參數gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}mysg.elem = epmysg.waitlink = nilgp.waiting = mysgmysg.g = gpmysg.selectdone = nilmysg.c = cgp.param = nil// 進入 channel 的等待接收隊列c.recvq.enqueue(mysg)// 將當前 goroutine 掛起goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)// 被喚醒了,接著從這里繼續執行一些掃尾工作if mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}closed := gp.param == nilgp.param = nilmysg.c = nil// 釋放當前 gorountine 的 sudogreleaseSudog(mysg)return true, !closed
}func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {// 如果是非緩沖型的 channelif c.dataqsiz == 0 {if raceenabled {racesync(c, sg)}// 未忽略接收的數據 不是 "<- ch",而是 "val <- ch",ep 指向 valif ep != nil {// 直接拷貝數據,從 sender goroutine -> receiver goroutinerecvDirect(c.elemtype, sg, ep)}} else {// 緩沖型的 channel,但 buf 已滿。// 1. 循環數組 buf 隊首的元素拷貝到接收數據的地址// 2. 將 sender 的數據入隊。qp := chanbuf(c, c.recvx)// …………// 將 recvx 處的數據拷貝給接收者if ep != nil {typedmemmove(c.elemtype, ep, qp)}// sender data -> buftypedmemmove(c.elemtype, qp, sg.elem)// 更新索引c.recvx++if c.recvx == c.dataqsiz {c.recvx = 0}c.sendx = c.recvx}sg.elem = nilgp := sg.g// 解鎖unlockf()gp.param = unsafe.Pointer(sg)if sg.releasetime != 0 {sg.releasetime = cputicks()}// 將當前處理器的 runnext 設置成發送數據的 Goroutine,在調度器下一次調度時將阻塞的發送方喚醒。goready(gp, skip+1)
}func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {// dst is on our stack or the heap, src is on another stack.src := sg.elemtypeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)memmove(dst, src, t.size)
}

從 channel 接收消息 的 核心函數是 chanrecv

跟 send 流程差不多:
特殊情況:

  1. 如果 channel 為空,那么會直接調用 runtime.gopark 掛起當前 goroutine。
  2. 如果 channel 已經關閉并且緩沖區沒有任何數據,runtime.chanrecv 會直接返回零值。
    正常情況:
  3. 如果 channel 的 sendq 隊列中存在掛起的 goroutine,會將 recvx 索引所在的數據拷貝到接收變量所在的內存空間上并將 sendq 隊列中 goroutine 的數據拷貝到緩沖區。
  4. 如果 channel 的緩沖區中包含數據,那么直接讀取 recvx 索引對應的數據。
  5. 在默認情況下會掛起當前的 goroutine,將 runtime.sudog 結構加入 recvq 隊列并陷入休眠等待調度器的喚醒。
    從 channel 接收數據時,會觸發 goroutine 調度的兩個時機:
  6. 當 channel 為空時。
  7. 當緩沖區中不存在數據并且也不存在數據的發送者時。

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

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

相關文章

【嵌入式——FreeRTOS】任務

【嵌入式——FreeRTOS】任務 任務創建和刪除動態方式創建任務靜態方式創建任務 刪除任務任務切換調度器任務切換流程 任務掛起任務恢復相關API函數 任務創建和刪除 動態方式創建任務 任務的任務控制塊以及任務的棧空間所需的內存&#xff0c;均由freeRTOS從freeRTOS管理的堆中…

c#asp.net中字典的使用

字典是一個鍵值對&#xff0c;可以用來保存數據&#xff0c;再查詢&#xff1b; 下面是一個案例&#xff1a;依據多個學號查詢多個學生的姓名&#xff0c;只能到數據庫查詢一次數據&#xff01;&#xff01;&#xff01; 先在數據庫查詢學號對應的學生&#xff0c;把數據保存在…

mysql8.0.19安裝zip版本

下載地址https://downloads.mysql.com/archives/community/ 下載版本 下載后解壓&#xff0c;不包括data 和my.ini文件。其中data 文件是自動生成的【mysqld --initialize --console】&#xff0c;my.ini需要自己編寫設置。 新建my.ini文件 需要自己設置 basedirG:\soft\mysql…

內網服務器時間校正

新購買的云服務器發現內網機器和可以訪問外網的機器時間慢了三分鐘&#xff0c;導致有些訪問會報錯&#xff0c;那么我們配置一下ntp校正一下時間。外網配置起來比較簡單&#xff0c;直接下載ntp執行校正命令即可。 比當前時間慢了三分鐘 注意當前服務器是可以訪問外網的機器這…

【gitee使用教程】(創建項目倉庫并上傳代碼簡易版)

gitee使用教程&#xff0c;創建項目倉庫并上傳代碼簡易版 1.在碼云上創建一個倉庫2.將代碼克隆到本地1.復制倉庫地址2.找到你想要放置的文件位置&#xff0c;右鍵點擊更多選項&#xff0c;選擇Git Clone3.將復制的倉庫地址填入URL 3. IDEA結合GIT和Gitee的簡單使用idea需要識別…

【python】最新版抖音s逆向拿到數據,非常詳細教程(附完整代碼)

?? 歡迎大家來到景天科技苑?? ???? 養成好習慣,先贊后看哦~???? ?? 作者簡介:景天科技苑 ??《頭銜》:大廠架構師,華為云開發者社區專家博主,阿里云開發者社區專家博主,CSDN全棧領域優質創作者,掘金優秀博主,51CTO博客專家等。 ??《博客》:Python全…

Excel 宏錄制與VBA編程 ——VBA編程技巧篇一 (Union方法、Resize方法、Cells方法、UseSelect方法、With用法)

Uniom方法 使用Union方法可以將多個非連續區域連接起來成為一個區域&#xff0c;從而可以實現對多個非連續區域一起進行操作。 Resize方法 使用Range對象的Resize屬性調整指定區域的大小&#xff0c;并返回調整大小后的單元格區域。 Cells方法 Cells屬性返回一個Range對象。 Us…

Domino應用中的HTML5

大家好&#xff0c;才是真的好。 在xpages多年不見有效更新&#xff0c;前景不明的時候&#xff0c;Domino傳統Web應用開發方式還是受到了應有的青睞。畢竟&#xff0c;在Nomad Web時代&#xff0c;連最傳統的Notes CS原生應用也突然煥發了勃勃生機一樣。 但&#xff0c;對有…

什么是strcmp函數

目錄 開頭1.什么是strcmp函數2.strcmp函數里的內部結構3.strcmp函數的實際運用(這里只列舉其一)腦筋急轉彎 結尾 開頭 大家好&#xff0c;我叫這是我58。今天&#xff0c;我們要來認識一下C語言中的strcmp函數。 1.什么是strcmp函數 strcmp函數來自于C語言中的頭文件<str…

ARP 原理詳解 一

ARP 原理 ARP&#xff08;Address Resolution Protocol&#xff09;地址解析協議&#xff0c;是根據 IP 地址獲取物理地址的一個 TCP/IP 協議。 OSI 網絡七層模型中&#xff0c;IP 地址在 OSI 模型第三層&#xff0c;MAC 地址在第二層&#xff0c;彼此不直接通信。 在通過以…

Spring Cloud Alibaba AI 大模型使用示例

1 pom.xml, 注意版本(jdk17) &#xff0c;倉庫地址&#xff0c;排除的依賴&#xff08;日志錯誤&#xff09; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www…

性能測試中的場景設計和測試執行

假設一個內部系統要求響應時間在 3s 以內&#xff0c;支持最大用戶數為4萬。根據二八原則&#xff0c;80%用戶在20%時間使用系統(4w80%)/(24h20%)≈1.9點擊/秒。并發數TPS&#xff08;運行時間思考時間&#xff09;1.9&#xff08;30.50.330.50.30.53&#xff09;21。 注意&am…

Flutter循序漸進==>數據結構(列表、映射和集合)和錯誤處理

導言 填鴨似的教育確實不行&#xff0c;我高中時學過集合&#xff0c;不知道有什么用&#xff0c;毫無興趣&#xff0c;等到我學了一門編程語言后&#xff0c;才發現集合真的很有用&#xff1b;可以去重&#xff0c;可以看你有我沒有的&#xff0c;可以看我有你沒有的&#xf…

毫米波雷達深度學習技術-1.7訓練一個神經網絡

1.7 訓練一個神經網絡 對于訓練神經網絡&#xff0c;有兩個步驟&#xff0c;即前向傳遞和誤差反向傳播。 1.7.1 前向傳播和反向傳播 在前向傳遞中&#xff0c;輸入被饋送到模型并與權重向量相乘&#xff0c;并為每一層添加偏差以計算模型的輸出。密集層或全連接層第l層的輸入、…

SQL面試題練習 —— 查詢前2大和前2小用戶并有序拼接

目錄 1 題目2 建表語句3 題解 1 題目 有用戶賬戶表&#xff0c;包含年份&#xff0c;用戶id和值,請按照年份分組&#xff0c;取出值前兩小和前兩大對應的用戶id&#xff0c;需要保持值最小和最大的用戶id排首位。 樣例數據 ------------------------- | year | user_id | v…

網絡基礎-RIP協議

RIP&#xff08;Routing Information Protocol&#xff09;是一個基于距離矢量的動態路由協議&#xff0c;常用于小型到中型網絡。RIP是較早的路由協議之一&#xff0c;具有簡單易用的特點。以下是關于RIP協議的詳細介紹&#xff1a; RIP的主要特點 ①使用跳數&#xff08;ho…

非標設備行業的數智化項目管理

近年來&#xff0c;中國制造快速發展&#xff0c;企業迫切需要加快轉型升級。與傳統制造業相比&#xff0c;高端制造業具有明顯的優勢&#xff1a;高技術、高附加值、低污染、低排放、競爭優勢強。一方面&#xff0c;企業對于生產效率和自動化水平的要求不斷提高&#xff0c;期…

開發個人OpenUI--1 項目介紹

開發個人OpenUI--1 項目介紹 開發個人OpenUI--1 項目介紹知識點大綱文章目錄項目地址 開發個人OpenUI–1 項目介紹 本文將以一個使用Ollama部署的ChatGPT為背景&#xff0c;主要還是介紹和學習使用 go-zero 框架&#xff0c;開發個人OpenUI的服務器后端&#xff0c;使用Docker…

武漢星起航:成功掛牌上股交,領航亞馬遜跨境電商,共創未來輝煌

在全球電商的競爭格局中&#xff0c;亞馬遜憑借其卓越的服務、豐富的商品種類和高效的物流體系&#xff0c;始終穩坐全球電商市場的頭把交椅。而在這股不可阻擋的電商浪潮中&#xff0c;武漢星起航電子商務有限公司憑借其前瞻性的戰略布局和強大的運營能力&#xff0c;成功在20…

名企面試必問30題(十二)——簡單介紹一下你的家庭情況

1.思路 對于面試官來說&#xff0c;他提出這個問題&#xff0c;只是為了深挖您的性格、穩定性、行事風格&#xff0c;包括未來定居規劃、生育規劃等基礎信息&#xff0c;這是正常情況。您不要過多圍繞其他家庭成員來講&#xff0c;否則面試官無法獲取他想要的&#xff0c;您也難…