日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Golang-channel实现

發布時間:2023/12/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Golang-channel实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.簡介

? ? ? ?如果說goroutine是Go語言程序的并發體的話,那么channels則是它們之間的通信機制。一個channel是一個通信系統,它可以讓一個goroutine通過它給另一個goroutine發送值信息。每個channel都有一個特定的類型,也就是channels可發送數據的類型。

二.使用

2.1 創建channel

ch := make(chan int)

2.2?channel讀寫

ch := make(chan int)// write to channel ch <- x// read from channel x <- ch// another way to read x = <- ch

channel 一定要初始化后才能進行讀寫操作,否則會永久阻塞。

2.3 關閉 channel

ch := make(chan int)close(ch)

有關 channel 的關閉,你需要注意以下事項:

  • 關閉一個未初始化(nil) 的 channel 會產生 panic
  • 重復關閉同一個 channel 會產生 panic
  • 向一個已關閉的 channel 中發送消息會產生 panic
  • 從已關閉的 channel 讀取消息不會產生 panic,且能讀出 channel 中還未被讀取的消息,若消息均已讀出,則會讀到類型的零值。從一個已關閉的 channel 中讀取消息永遠不會阻塞,并且會返回一個為 false 的 ok-idiom,可以用它來判斷 channel 是否關閉
  • 關閉 channel 會產生一個廣播機制,所有向 channel 讀取消息的 goroutine 都會收到消息

三.channel的兩種類型

3.1 有緩沖channel

ch := make(chan int, 10)

? ? ? ?有緩存的 channel 類似一個阻塞隊列(采用環形數組實現)。當緩存未滿時,向 channel 中發送消息時不會阻塞,當緩存滿時,發送操作將被阻塞,直到有其他 goroutine 從中讀取消息;相應的,當 channel 中消息不為空時,讀取消息不會出現阻塞,當 channel 為空時,讀取操作會造成阻塞,直到有 goroutine 向 channel 中寫入消息。

3.2 無緩沖channel

ch := make(chan int)

? ? ? ?從無緩存的 channel 中讀取消息會阻塞,直到有 goroutine 向該 channel 中發送消息;同理,向無緩存的 channel 中發送消息也會阻塞,直到有 goroutine 從 channel 中讀取消息。

3.3??select 使用

select 用法類似 IO 多路復用,可以同時監聽多個 channel 的消息狀態,看下面的例子

select {case <- ch1:...case <- ch2:...case ch3 <- 10;...default:... }

???????

msgCh := make(chan struct{}) quitCh := make(chan struct{}) for {select {case <- msgCh:doWork()case <- quitCh:finish()return }
  • select 可以同時監聽多個 channel 的寫入或讀取
  • 執行 select 時,若只有一個 case 通過(不阻塞),則執行這個 case 塊
  • 若有多個 case 通過,則隨機挑選一個 case 執行
  • 若所有 case 均阻塞,且定義了 default 模塊,則執行 default 模塊。若未定義 default 模塊,則 select 語句阻塞,直到有 case 被喚醒。
  • 使用 break 會跳出 select 塊。

三.底層實現

2.1 hchan結構

type hchan struct {qcount uint // 隊列中當前數據的個數dataqsiz uint // size of the circular queuebuf unsafe.Pointer // 數據緩沖區,存放數據的環形數組elemsize uint16 // channel中數據類型的大小(單個元素的大小)closed uint32 // 表示channel是否關閉標識位elemtype *_type // 隊列中的元素類型sendx uint // 當前發送元素的索引recvx uint // 當前接收元素的索引recvq waitq // 接受等待隊列,由recv行為(也就是<-ch)阻塞在channel上的goroutine隊列sendq waitq // 發送等待隊列, 由send行為(也就是ch<-)阻塞在channel上的goroutine隊列//lock保護chann中的所有字段,以及在此通道上阻塞的sudoG中的幾個字段。//保持此鎖時不要更改另一個G狀態(特別是沒準備好G),因為這可能會因堆棧收縮而死鎖lock mutex }//發送及接收隊列的·1結構體 type waitq struct {first *sudoglast *sudog }
  • qcount uint // 當前隊列中剩余元素個數。
  • dataqsiz uint // 環形隊列長度,即緩沖區的大小,即make(chan T,N),N。
  • buf unsafe.Pointer // 環形隊列指針。
  • elemsize uint16 // 每個元素的大小。
  • closed uint32 // 表示當前通道是否處于關閉狀態。創建通道后,該字段設置為0,即通道打開; 通過調用close將其設置為1,通道關閉。
  • elemtype *_type // 元素類型,用于數據傳遞過程中的賦值。
  • sendx uint 和?recvx uint是環形緩沖區的狀態字段,它指示緩沖區的當前索引 - 支持數組,它可以從中發送數據和接收數據。
  • recvq waitq // 等待讀消息的goroutine隊列。
  • sendq waitq // 等待寫消息的goroutine隊列。
  • lock mutex // 互斥鎖,為每個讀寫操作鎖定通道,因為發送和接收必須是互斥操作。

2.2 創建過程

2.2.1 寫入操作

1.創建帶buffer的channel

2.向channel中寫入數據

3.3 寫入過程如下:

  • 鎖定整個管道結構。
  • 確定寫入,嘗試從等會帶隊列等待goroutine,然后將元素直接寫入goroutine。
  • 如果recvq為空,則確定緩沖區是否可用。如果可用,從當前goroutine復制數據到緩沖區。
  • 如果緩沖區已滿,則要寫入的元素將保存在當前正在執行的goroutine結構中,并且當前goroutine將在sendq中排隊并從運行中掛起。
  • 寫入完成釋放鎖。

2.2.2 讀取過程

  • 先讀取channel全局鎖。
  • 嘗試sendq從等待隊列中獲取等待的goroutine。
  • 如果有等待的goroutine,且有緩沖區(緩沖區已滿),從緩沖區隊首取出數據,再從sendq取出一個goroutine。將goroutine中數據存入buf對位,結束讀取釋放鎖。
  • 如沒有后等待的goroutine,且緩沖區有數據,直接讀取緩沖區數據,解釋讀取釋放鎖。
  • 如果沒有等待的goroutine,且沒有緩沖或緩沖區域為空,將當前的goroutine加入denq排隊,進入睡眠,等待被寫goroutine喚醒。結束釋放鎖。

總結

以上是生活随笔為你收集整理的Golang-channel实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。