91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用golang channel的諸多特性和技巧

馬哥Linux運維 ? 來源:GoDaddy. ? 作者:GoDaddy. ? 2021-09-06 15:14 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本文介紹了使用 golang channel 的諸多特性和技巧,已經熟悉了 go 語言特性的小伙伴也可以看看,很有啟發(fā)。 不同于傳統(tǒng)的多線程并發(fā)模型使用共享內存來實現線程間通信的方式,golang 的哲學是通過 channel 進行協(xié)程 (goroutine) 之間的通信來實現數據共享:

Do not communicate by sharing memory; instead, share memory by communicating.

這種方式的優(yōu)點是通過提供原子的通信原語,避免了競態(tài)情形 (race condition) 下復雜的鎖機制。channel 可以看成一個 FIFO 隊列,對 FIFO 隊列的讀寫都是原子的操作,不需要加鎖。對 channel 的操作行為結果總結如下:

操作 nil channel closed channel not-closed non-nil channel
close panic panic 成功 close
寫ch <- 一直阻塞 panic 阻塞或成功寫入數據
讀<- ch 一直阻塞 讀取對應類型零值 阻塞或成功讀取數據

讀取一個已關閉的 channel 時,總是能讀取到對應類型的零值,為了和讀取非空未關閉 channel 的行為區(qū)別,可以使用兩個接收值:

//okisfalsewhenchisclosed v,ok:=<-chgolang 中大部分類型都是值類型(只有 slice / channel / map 是引用類型),讀/寫類型是值類型的 channel 時,如果元素 size 比較大時,應該使用指針代替,避免頻繁的內存拷貝開銷。

內部實現

如圖所示,在 channel 的內部實現中(具體定義在$GOROOT/src/runtime/chan.go里),維護了 3 個隊列:

讀等待協(xié)程隊列 recvq,維護了阻塞在讀此 channel 的協(xié)程列表

寫等待協(xié)程隊列 sendq,維護了阻塞在寫此 channel 的協(xié)程列表

緩沖數據隊列 buf,用環(huán)形隊列實現,不帶緩沖的 channel 此隊列 size 則為 0

img 當協(xié)程嘗試從未關閉的 channel 中讀取數據時,內部的操作如下:

當 buf 非空時,此時 recvq 必為空,buf 彈出一個元素給讀協(xié)程,讀協(xié)程獲得數據后繼續(xù)執(zhí)行,此時若 sendq 非空,則從 sendq 中彈出一個寫協(xié)程轉入 running 狀態(tài),待寫數據入隊列 buf ,此時讀取操作<- ch?未阻塞;

當 buf 為空但 sendq 非空時(不帶緩沖的 channel),則從 sendq 中彈出一個寫協(xié)程轉入 running 狀態(tài),待寫數據直接傳遞給讀協(xié)程,讀協(xié)程繼續(xù)執(zhí)行,此時讀取操作<- ch?未阻塞;

當 buf 為空并且 sendq 也為空時,讀協(xié)程入隊列 recvq 并轉入 blocking 狀態(tài),當后續(xù)有其他協(xié)程往 channel 寫數據時,讀協(xié)程才會重新轉入 running 狀態(tài),此時讀取操作<- ch?阻塞。

類似的,當協(xié)程嘗試往未關閉的 channel 中寫入數據時,內部的操作如下:

當隊列 recvq 非空時,此時隊列 buf 必為空,從 recvq 彈出一個讀協(xié)程接收待寫數據,此讀協(xié)程此時結束阻塞并轉入 running 狀態(tài),寫協(xié)程繼續(xù)執(zhí)行,此時寫入操作ch <-?未阻塞;

當隊列 recvq 為空但 buf 未滿時,此時 sendq 必為空,寫協(xié)程的待寫數據入 buf 然后繼續(xù)執(zhí)行,此時寫入操作ch <-?未阻塞;

當隊列 recvq 為空并且 buf 為滿時,此時寫協(xié)程入隊列 sendq 并轉入 blokcing 狀態(tài),當后續(xù)有其他協(xié)程從 channel 中讀數據時,寫協(xié)程才會重新轉入 running 狀態(tài),此時寫入操作ch <-?阻塞。

當關閉 non-nil channel 時,內部的操作如下:

當隊列 recvq 非空時,此時 buf 必為空,recvq 中的所有協(xié)程都將收到對應類型的零值然后結束阻塞狀態(tài);

當隊列 sendq 非空時,此時 buf 必為滿,sendq 中的所有協(xié)程都會產生 panic ,在 buf 中數據仍然會保留直到被其他協(xié)程讀取。

使用場景

除了常規(guī)的用來在協(xié)程之間傳遞數據外,本節(jié)列出了一些特殊的使用 channel 的場景。

futures / promises

golang 雖然沒有直接提供 futrue / promise 模型的操作原語,但通過 goroutine 和 channel 可以實現類似的功能:

packagemain import( "io/ioutil" "log" "net/http" ) //RequestFuture,httprequestpromise. funcRequestFuture(urlstring)<-chan?[]byte?{ ????c?:=?make(chan?[]byte,?1) ????go?func()?{ ????????var?body?[]byte ????????defer?func()?{ ????????????c?<-?body ????????}() ????????res,?err?:=?http.Get(url) ????????if?err?!=?nil?{ ????????????return ????????} ????????defer?res.Body.Close() ????????body,?_?=?ioutil.ReadAll(res.Body) ????}() ????return?c } func?main()?{ ????future?:=?RequestFuture("https://api.github.com/users/octocat/orgs") ????body?:=?<-future ????log.Printf("reponse?length:?%d",?len(body)) }

條件變量 (condition variable)

類型于 POSIX 接口中線程通知其他線程某個事件發(fā)生的條件變量,channel 的特性也可以用來當成協(xié)程之間同步的條件變量。因為 channel 只是用來通知,所以 channel 中具體的數據類型和值并不重要,這種場景一般用strct {}作為 channel 的類型。

一對一通知

類似pthread_cond_signal()的功能,用來在一個協(xié)程中通知另個某一個協(xié)程事件發(fā)生:

packagemain import( "fmt" "time" ) funcmain(){ ch:=make(chanstruct{}) nums:=make([]int,100) gofunc(){ time.Sleep(time.Second) fori:=0;i

廣播通知

類似pthread_cond_broadcast()的功能。利用從已關閉的 channel 讀取數據時總是非阻塞的特性,可以實現在一個協(xié)程中向其他多個協(xié)程廣播某個事件發(fā)生的通知:

packagemain import( "fmt" "time" ) funcmain(){ N:=10 exit:=make(chanstruct{}) done:=make(chanstruct{},N) //startNworkergoroutines fori:=0;i

信號

channel 的讀/寫相當于信號量的 P / V 操作,下面的示例程序中 channel 相當于信號量:

packagemain import( "log" "math/rand" "time" ) typeSeatint typeBarchanSeat func(barBar)ServeConsumer(customerIdint){ log.Print("->consumer#",customerId,"entersthebar") seat:=<-bar?//?need?a?seat?to?drink ????log.Print("consumer#",?customerId,?"?drinks?at?seat#",?seat) ????time.Sleep(time.Second?*?time.Duration(2+rand.Intn(6))) ????log.Print("<-?consumer#",?customerId,?"?frees?seat#",?seat) ????bar?<-?seat?//?free?the?seat?and?leave?the?bar } func?main()?{ ????rand.Seed(time.Now().UnixNano()) ????bar24x7?:=?make(Bar,?10)?//?the?bar?has?10?seats ????//?Place?seats?in?an?bar. ????for?seatId?:=?0;?seatId?

互斥量

互斥量相當于二元信號里,所以 cap 為 1 的 channel 可以當成互斥量使用:

packagemain import"fmt" funcmain(){ mutex:=make(chanstruct{},1)//thecapacitymustbeone counter:=0 increase:=func(){ mutex<-?struct{}{}?//?lock ????????counter++ ????????<-mutex?//?unlock ????} ????increase1000?:=?func(done?chan<-?struct{})?{ ????????for?i?:=?0;?i?

關閉 channel

關閉不再需要使用的 channel 并不是必須的。跟其他資源比如打開的文件、socket 連接不一樣,這類資源使用完后不關閉后會造成句柄泄露,channel 使用完后不關閉也沒有關系,channel 沒有被任何協(xié)程用到后最終會被 GC 回收。關閉 channel 一般是用來通知其他協(xié)程某個任務已經完成了。golang 也沒有直接提供判斷 channel 是否已經關閉的接口,雖然可以用其他不太優(yōu)雅的方式自己實現一個:

funcisClosed(chchanint)bool{ select{ case<-ch: ????????return?true ????default: ????} ????return?false }不過實現一個這樣的接口也沒什么必要。因為就算通過?isClosed()?得到當前 channel 當前還未關閉,如果試圖往 channel 里寫數據,仍然可能會發(fā)生 panic ,因為在調用?isClosed()?后,其他協(xié)程可能已經把 channel 關閉了。關閉 channel 時應該注意以下準則:

不要在讀取端關閉 channel ,因為寫入端無法知道 channel 是否已經關閉,往已關閉的 channel 寫數據會 panic ;

有多個寫入端時,不要再寫入端關閉 channle ,因為其他寫入端無法知道 channel 是否已經關閉,關閉已經關閉的 channel 會發(fā)生 panic ;

如果只有一個寫入端,可以在這個寫入端放心關閉 channel 。

關閉 channel 粗暴一點的做法是隨意關閉,如果產生了 panic 就用 recover 避免進程掛掉。稍好一點的方案是使用標準庫的sync包來做關閉 channel 時的協(xié)程同步,不過使用起來也稍微復雜些。下面介紹一種優(yōu)雅些的做法。

一寫多讀

這種場景下這個唯一的寫入端可以關閉 channel 用來通知讀取端所有數據都已經寫入完成了。讀取端只需要用for range把 channel 中數據遍歷完就可以了,當 channel 關閉時,for range仍然會將 channel 緩沖中的數據全部遍歷完然后再退出循環(huán):

packagemain import( "fmt" "sync" ) funcmain(){ wg:=&sync.WaitGroup{} ch:=make(chanint,100) send:=func(){ fori:=0;i

多寫一讀

這種場景下雖然可以用sync.Once來解決多個寫入端重復關閉 channel 的問題,但更優(yōu)雅的辦法設置一個額外的 channel ,由讀取端通過關閉來通知寫入端任務完成不要再繼續(xù)再寫入數據了:

packagemain import( "fmt" "sync" ) funcmain(){ wg:=&sync.WaitGroup{} ch:=make(chanint,100) done:=make(chanstruct{}) send:=func(idint){ deferwg.Done() fori:=0;;i++{ select{ case<-done: ????????????????//?get?exit?signal ????????????????fmt.Printf("sender?#%d?exit ",?id) ????????????????return ????????????case?ch?<-?id*1000?+?i: ????????????} ????????} ????} ????recv?:=?func()?{ ????????count?:=?0 ????????for?i?:=?range?ch?{ ????????????fmt.Printf("receiver?get?%d ",?i) ????????????count++ ????????????if?count?>=1000{ //signalrecvingfinish close(done) return } } } wg.Add(3) gosend(0) gosend(1) gosend(2) recv() wg.Wait() }

多寫多讀

這種場景稍微復雜,和上面的例子一樣,也需要設置一個額外 channel 用來通知多個寫入端和讀取端。另外需要起一個額外的協(xié)程來通過關閉這個 channel 來廣播通知:

packagemain import( "fmt" "sync" "time" ) funcmain(){ wg:=&sync.WaitGroup{} ch:=make(chanint,100) done:=make(chanstruct{}) send:=func(idint){ deferwg.Done() fori:=0;;i++{ select{ case<-done: ????????????????//?get?exit?signal ????????????????fmt.Printf("sender?#%d?exit ",?id) ????????????????return ????????????case?ch?<-?id*1000?+?i: ????????????} ????????} ????} ????recv?:=?func(id?int)?{ ????????defer?wg.Done() ????????for?{ ????????????select?{ ????????????case?<-done: ????????????????//?get?exit?signal ????????????????fmt.Printf("receiver?#%d?exit ",?id) ????????????????return ????????????case?i?:=?<-ch: ????????????????fmt.Printf("receiver?#%d?get?%d ",?id,?i) ????????????????time.Sleep(time.Millisecond) ????????????} ????????} ????} ????wg.Add(6) ????go?send(0) ????go?send(1) ????go?send(2) ????go?recv(0) ????go?recv(1) ????go?recv(2) ????time.Sleep(time.Second) ????//?signal?finish ????close(done) ????//?wait?all?sender?and?receiver?exit ????wg.Wait() }

總結

channle 作為 golang 最重要的特性,用起來還是比較爽的。傳統(tǒng)的 C 里要實現類型的功能的話,一般需要用到 socket 或者 FIFO 來實現,另外還要考慮數據包的完整性與并發(fā)沖突的問題,channel 則屏蔽了這些底層細節(jié),使用者只需要考慮讀寫就可以了。channel 是引用類型,了解一下 channel 底層的機制對更好的使用 channel 還是很用必要的。

雖然操作原語簡單,但涉及到阻塞的問題,使用不當可能會造成死鎖或者無限制的協(xié)程創(chuàng)建最終導致進程掛掉。 channel 除在可以用來在協(xié)程之間通信外,其阻塞和喚醒協(xié)程的特性也可以用作協(xié)程之間的同步機制,文中也用示例簡單介紹了這種場景下的用法。

關閉 channel 并不是必須的,只要沒有協(xié)程沒用引用 channel ,最終會被 GC 清理。所以使用的時候要特別注意,不要讓協(xié)程阻塞在 channel 上,這種情況很難檢測到,而且會造成 channel 和阻塞在 channel 的協(xié)程占有的資源無法被 GC 清理最終導致內存泄露。

channle 方便 golang 程序使用 CSP 的編程范形,但是 golang 是一種多范形的編程語言,golang 也支持傳統(tǒng)的通過共享內存來通信的編程方式。終極的原則是根據場景選擇合適的編程范型,不要因為 channel 好用而濫用 CSP 。

轉自:http://litang.me/post/golang-channel/

編輯:jq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • 阻塞
    +關注

    關注

    0

    文章

    24

    瀏覽量

    8436
  • root
    +關注

    關注

    1

    文章

    86

    瀏覽量

    22109
  • go語言
    +關注

    關注

    1

    文章

    159

    瀏覽量

    9785

原文標題:golang channel 使用總結

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    深入解析CSD16413Q5A N-Channel NexFET? Power MOSFET

    的CSD16413Q5A N-Channel NexFET? Power MOSFET。 文件下載: csd16413q5a.pdf 產品特性 CSD16413Q5A具有諸多令人矚目的特性
    的頭像 發(fā)表于 03-06 16:35 ?952次閱讀

    深入解析CSD16410Q5A N-Channel NexFET? Power MOSFET

    深入解析CSD16410Q5A N-Channel NexFET? Power MOSFET 在電子設計領域,功率MOSFET是至關重要的元件,它在電源轉換、信號處理等諸多應用中發(fā)揮著關鍵作用。今天
    的頭像 發(fā)表于 03-06 16:30 ?511次閱讀

    CSD17308Q3 30 - V N - Channel NexFET? Power MOSFET深度解析

    。今天我們要探討的CSD17308Q3 30 - V N - Channel NexFET? Power MOSFET,具有諸多優(yōu)秀特性,能為工程師們的設計帶來更多便利和可能性。 文件下載
    的頭像 發(fā)表于 03-06 15:15 ?85次閱讀

    深入解析CSD17304Q3:30V N-Channel NexFET? Power MOSFETs

    的作用。今天我們要深入探討的是德州儀器(TI)推出的CSD17304Q3,一款專為5V柵極驅動應用優(yōu)化的30V N-Channel NexFET? Power MOSFET。它具有諸多優(yōu)異特性,適用于
    的頭像 發(fā)表于 03-06 14:55 ?76次閱讀

    30V N-Channel NexFET? Power MOSFET CSD17322Q5A:設計與應用解析

    (TI)的30V N - Channel NexFET? Power MOSFET CSD17322Q5A,了解其特性、參數及應用場景。 文件下載: csd17322q5a.pdf 產品概述
    的頭像 發(fā)表于 03-06 14:10 ?78次閱讀

    CSD17576Q5B 30V N - Channel NexFET? Power MOSFET 深度解析

    概述 CSD17576Q5B 是一款 30V、1.7mΩ 的 SON 5x6 - mm NexFET? 功率 MOSFET,專為降低功率轉換應用中的損耗而設計。它采用了先進的技術,具有諸多優(yōu)秀特性,適用于多種應用場景。 1.1 產品特性
    的頭像 發(fā)表于 03-05 16:05 ?81次閱讀

    CSD25202W15 20-V P-Channel NexFET? Power MOSFET 技術詳解

    性能和特性對于整個系統(tǒng)的效率和穩(wěn)定性起著至關重要的作用。CSD25202W15 20-V P-Channel NexFET? Power MOSFET 以其獨特的設計和優(yōu)異的性能,成為眾多電池管理和保護
    的頭像 發(fā)表于 03-05 15:45 ?76次閱讀

    探索CSD23202W10 12-V P-Channel NexFET? Power MOSFET:特性、應用與設計要點

    探索CSD23202W10 12-V P-Channel NexFET? Power MOSFET:特性、應用與設計要點 在電子設計領域,功率MOSFET是至關重要的元件,它直接影響著電路的性能
    的頭像 發(fā)表于 03-05 15:15 ?88次閱讀

    深度剖析CSD22204W -8 V P-Channel NexFET? Power MOSFET

    1.1 產品特性 CSD22204W具有諸多出色的特性: 低電阻與小尺寸 :采用1.5 mm × 1.5 mm的小尺寸封裝,同時具備低導通電阻,在有限的空間內實現高效的功率轉換。 環(huán)保設計 :符合無鉛
    的頭像 發(fā)表于 03-05 14:35 ?143次閱讀

    CSD19505KTT 80V N-Channel NexFET? Power MOSFET:特性、應用與技術解析

    CSD19505KTT 80V N-Channel NexFET? Power MOSFET:特性、應用與技術解析 在電子設計領域,功率MOSFET作為關鍵元件,對提高電源轉換效率、降低功耗起著
    的頭像 發(fā)表于 03-05 11:25 ?151次閱讀

    CSD25480F3 -20-V P-Channel FemtoFET? MOSFET技術解析

    和應用這款產品。 文件下載: csd25480f3.pdf 二、產品特性 2.1 產品概述 CSD25480F3具有諸多突出特性。它擁有低導通電阻,能夠有效降低功率損耗;超低的
    的頭像 發(fā)表于 03-05 11:15 ?158次閱讀

    探究CSD15380F3 20 - V N - Channel FemtoFET? MOSFET:特性、應用及設計要點

    探究CSD15380F3 20 - V N - Channel FemtoFET? MOSFET:特性、應用及設計要點 電子工程師在進行硬件設計時,對于元器件的性能和特性有著極高的要求。今天我們就來
    的頭像 發(fā)表于 03-05 11:00 ?120次閱讀

    深入剖析 FCB290N80 N-Channel SuperFET? II MOSFET

    ON Semiconductor 的一部分。這款 MOSFET 具有諸多優(yōu)異特性,適用于多種重要應用場景。 文件下載: FCB290N80-D.pdf 二、產品背景與編號變更 Fairchild Semiconductor 被 ON Semiconductor 整合后,
    的頭像 發(fā)表于 01-26 17:05 ?306次閱讀

    【HZ-T536開發(fā)板免費體驗】5、安裝sqlite3和使用golang讀寫數據庫

    如果想在嵌入式設備上實現簡單的設備管理功能,需要數據庫和服務后端程序。服務端程序,我更傾向使用golang來實現。 安裝sqlite3,使用ubuntu環(huán)境,可以直接用apt install安裝程序
    發(fā)表于 08-26 00:04

    Stellar P6 SARADC模塊,Internal channel/Test channel/External channel的都有那些區(qū)別呢?

    關于SARADC模塊,請問Internal channel/Test channel/External channel的都有那些區(qū)別呢 ,應用場景有何不同。Supervisor ADC和普通ADC怎么配合使用呢?
    發(fā)表于 03-12 07:34