golang中for循環(huán)遍歷channel時需要注意的問題詳解
前言
for循環(huán)是Go語言唯一的循環(huán)結(jié)構(gòu),最近在做一個基于RabbitMQ的應(yīng)用,由于官方的qos沒有g(shù)olang的版本,所以出了一點問題。
問題代碼如下:
_, ch, err := component.NewRabbitMQ() if err != nil { panic(err) } if err := ch.Qos(10, 0, true); err != nil { panic(err) } msgs, err := ch.Consume("push", "", false, false, false, false, nil) if err != nil { panic(err) } for m := range msgs { go func(d *amqp.Delivery) { defer func() { d.Ack(false) } // 處理消息 }(&m) }
發(fā)現(xiàn)消費到10條消息,進程就退出了,但是exit code為0,表示系統(tǒng)是正常退出,由于做了日志記錄可以確定消費了10條,所以初步確定是qos相關(guān)問題。
排查過程
- 首先是把d的tag打印出來,發(fā)現(xiàn)全部是一樣的,可以確定是重復(fù)的一條消息
- 一開始想到可能是經(jīng)典的go協(xié)程執(zhí)行在for循環(huán)結(jié)束以后導(dǎo)致的,但是看我的代碼不屬于這種情況,有使用&m保證每一條消息都是不同循環(huán)傳入的。所以判斷可能是for循環(huán)的傳遞問題。
- 確定方向之后開始寫了一個測試項目用來驗證我的想法是否正確。
測試代碼
package main import "fmt" func main() { ch := make(chan int, 10) for i := 0; i < 10; i++ { ch <- i } close(ch) for v := range ch { fmt.Println(&v) } }
執(zhí)行輸出
0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008 0xc420086008
到這里才煥然大悟,for循環(huán)中,如果循環(huán)變量不是指針,那么每次的變量是同一個,不過值變了。,所以上例中的RabbitMQ go協(xié)程消費消息那里,需要直接傳遞值而不是指針,經(jīng)過測試之后發(fā)現(xiàn),問題確實解決了。
題外話
測試代碼那里,如果不close掉channel是會發(fā)生死鎖的,原因是 當(dāng)for循環(huán)讀完channel的10個值之后會繼續(xù)嘗試讀取下一個,而由于channel為空又沒關(guān)閉,會一直阻塞形成死鎖
TOOD
研究RabbitMQ Consumer部分的源碼來看看消費channel被關(guān)閉的問題。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Golang?統(tǒng)計字符串中數(shù)字字母數(shù)量的實現(xiàn)方法
這篇文章主要介紹了Golang?統(tǒng)計字符串中數(shù)字字母數(shù)量,本文給出了兩種從字符串獲取數(shù)字與字母數(shù)量的方法,分別是ASCII 碼值和正則表達式,需要的朋友可以參考下2022-06-06go?zero微服務(wù)實戰(zhàn)處理每秒上萬次的下單請求
這篇文章主要為大家介紹了go?zero微服務(wù)實戰(zhàn)處理每秒上萬次的下單請求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07vscode中安裝Go插件和配置Go環(huán)境詳細步驟
要在VSCode中配置Go語言插件,首先需要確保你的電腦已經(jīng)安裝了Go環(huán)境和最新版本的VSCode,這篇文章主要給大家介紹了關(guān)于vscode中安裝Go插件和配置Go環(huán)境的相關(guān)資料,需要的朋友可以參考下2024-01-01