4個(gè)場(chǎng)景教會(huì)你Go中Goroutine和通道是怎么用的
開(kāi)篇
這段時(shí)間把主要精力都放在了K8S上,差點(diǎn)把Golang給忘了。那本篇就分享一下并發(fā)相關(guān)的內(nèi)容(Goroutine和通道)。 本篇給出4個(gè)場(chǎng)景,這4個(gè)場(chǎng)景是在運(yùn)維開(kāi)發(fā)工作中較為常見(jiàn)的且也是比較典型的場(chǎng)景。通過(guò)這些代碼示例,讓你知道Goroutine和通道在運(yùn)維開(kāi)發(fā)中是怎么應(yīng)用的??偠灾远傊?,當(dāng)涉及到處理并發(fā)和并行任務(wù)時(shí),Goroutine和通道是非常強(qiáng)悍的,可以讓我們開(kāi)發(fā)出高效的、牛逼的并發(fā)程序。
實(shí)戰(zhàn)場(chǎng)景
1.并發(fā)執(zhí)行任務(wù)的場(chǎng)景
場(chǎng)景:假設(shè)需要編寫(xiě)一個(gè)程序,用于批量執(zhí)行某個(gè)操作(例如部署應(yīng)用程序、更新配置等)到多臺(tái)服務(wù)器上。
供參考的代碼示例:
package?main import?( ?"fmt" ?"sync" ) //?服務(wù)器結(jié)構(gòu)體 type?Server?struct?{ ?Name?string ?//?其他服務(wù)器相關(guān)的字段 } //?執(zhí)行任務(wù)的函數(shù) func?executeTask(server?Server,?task?string)?{ ?//?連接服務(wù)器并執(zhí)行任務(wù)的邏輯 ?fmt.Printf("執(zhí)行任務(wù)?[%s]?到服務(wù)器?[%s]\n",?task,?server.Name) ?//?執(zhí)行操作的代碼 } func?main()?{ ?//?服務(wù)器列表 ?servers?:=?[]Server{ ??{Name:?"Server1"}, ??{Name:?"Server2"}, ??{Name:?"Server3"}, ??//?添加更多的服務(wù)器 ?} ?//?任務(wù)列表 ?tasks?:=?[]string{"部署應(yīng)用程序",?"更新配置",?"執(zhí)行命令",?"其他任務(wù)"} ?//?創(chuàng)建一個(gè)任務(wù)通道,用于發(fā)送任務(wù)到Goroutine池 ?taskChannel?:=?make(chan?string) ?//?創(chuàng)建一個(gè)等待組,用于等待所有Goroutine執(zhí)行完畢 ?var?wg?sync.WaitGroup ?wg.Add(len(servers)) ?//?啟動(dòng)Goroutine池 ?for?_,?server?:=?range?servers?{ ??go?func(server?Server)?{ ???//?標(biāo)記任務(wù)完成時(shí),通知等待組減少一個(gè)計(jì)數(shù) ???defer?wg.Done() ???//?從任務(wù)通道中接收任務(wù),并執(zhí)行 ???for?task?:=?range?taskChannel?{ ????executeTask(server,?task) ???} ??}(server) ?} ?//?將任務(wù)發(fā)送到任務(wù)通道 ?for?_,?task?:=?range?tasks?{ ??taskChannel?<-?task ?} ?//?關(guān)閉任務(wù)通道,表示所有任務(wù)都已發(fā)送 ?close(taskChannel) ?//?等待所有Goroutine執(zhí)行完畢 ?wg.Wait() }
上面的代碼,創(chuàng)建了一個(gè)Goroutine池,每個(gè)Goroutine代表一臺(tái)服務(wù)器,通過(guò)通道將任務(wù)分發(fā)給Goroutine進(jìn)行并發(fā)執(zhí)行。每個(gè)Goroutine負(fù)責(zé)連接到服務(wù)器,并執(zhí)行相應(yīng)的操作。這樣可以加速任務(wù)的執(zhí)行,同時(shí)提高資源利用率。
2.并發(fā)日志處理的場(chǎng)景
場(chǎng)景:假設(shè)需要將大量的日志數(shù)據(jù)并發(fā)地寫(xiě)入到不同的目標(biāo)中(例如文件、數(shù)據(jù)庫(kù)、消息隊(duì)列等)。
供參考的代碼示例:
package?main import?( ?"fmt" ?"log" ?"os" ?"sync" ) //?日志結(jié)構(gòu)體 type?Log?struct?{ ?Message?string ?//?其他日志相關(guān)的字段 } func?main()?{ ?//?創(chuàng)建一個(gè)日志通道,用于發(fā)送日志數(shù)據(jù) ?logChannel?:=?make(chan?Log) ?//?創(chuàng)建一個(gè)等待組,用于等待所有Goroutine執(zhí)行完畢 ?var?wg?sync.WaitGroup ?//?啟動(dòng)一個(gè)Goroutine處理日志寫(xiě)入操作 ?wg.Add(1) ?go?func()?{ ??//?標(biāo)記日志寫(xiě)入完畢時(shí),通知等待組減少一個(gè)計(jì)數(shù) ??defer?wg.Done() ??//?打開(kāi)文件進(jìn)行日志寫(xiě)入 ??file,?err?:=?os.OpenFile("log.txt",?os.O_APPEND|os.O_CREATE|os.O_WRONLY,?0644) ??if?err?!=?nil?{ ???log.Fatal(err) ??} ??defer?file.Close() ??//?創(chuàng)建一個(gè)日志寫(xiě)入器 ??logger?:=?log.New(file,?"",?log.LstdFlags) ??//?從日志通道中接收日志數(shù)據(jù),并寫(xiě)入到目標(biāo)中 ??for?log?:=?range?logChannel?{ ???logger.Println(log.Message) ??} ?}() ?//?并發(fā)地向日志通道發(fā)送日志數(shù)據(jù) ?for?i?:=?0;?i?<?10;?i++?{ ??wg.Add(1) ??go?func(index?int)?{ ???//?標(biāo)記發(fā)送完日志數(shù)據(jù)時(shí),通知等待組減少一個(gè)計(jì)數(shù) ???defer?wg.Done() ???//?構(gòu)造日志數(shù)據(jù) ???log?:=?Log{ ????Message:?fmt.Sprintf("日志消息?%d",?index), ????//?設(shè)置其他日志字段 ???} ???//?發(fā)送日志數(shù)據(jù)到日志通道 ???logChannel?<-?log ??}(i) ?} ?//?關(guān)閉日志通道,表示所有日志數(shù)據(jù)都已發(fā)送 ?close(logChannel) ?//?等待日志寫(xiě)入操作的Goroutine執(zhí)行完畢 ?wg.Wait() }
在上面的代碼中,使用了一個(gè)專(zhuān)門(mén)的Goroutine來(lái)處理日志寫(xiě)入操作,該Goroutine從一個(gè)日志通道中讀取日志數(shù)據(jù),并將其寫(xiě)入到目標(biāo)中。其他的Goroutine可以并發(fā)地向該通道發(fā)送日志數(shù)據(jù),而不會(huì)因?yàn)閷?xiě)入操作而阻塞。
3.異步任務(wù)調(diào)度的場(chǎng)景
在實(shí)際的運(yùn)維工作中,有種場(chǎng)景是需要按照一定的調(diào)度策略異步執(zhí)行一些任務(wù)。比如這樣的場(chǎng)景:定期備份數(shù)據(jù)庫(kù)、定時(shí)清理無(wú)效數(shù)據(jù)等。
供參考的代碼示例:
package?main import?( ?"fmt" ?"time" ) //?執(zhí)行任務(wù)的函數(shù) func?executeTask(task?string)?{ ?//?執(zhí)行任務(wù)的邏輯 ?fmt.Println("執(zhí)行任務(wù):",?task) ?//?具體的任務(wù)操作代碼 } func?main()?{ ?//?創(chuàng)建一個(gè)定時(shí)器,每隔一段時(shí)間觸發(fā)一次 ?timer?:=?time.NewTimer(5?*?time.Second) ?//?啟動(dòng)一個(gè)Goroutine等待定時(shí)器的觸發(fā)事件 ?go?func()?{ ??//?等待定時(shí)器的觸發(fā)事件 ??<-timer.C ??//?定時(shí)器觸發(fā)后執(zhí)行任務(wù) ??executeTask("定時(shí)任務(wù)1") ??//?重新設(shè)置定時(shí)器,以實(shí)現(xiàn)循環(huán)調(diào)度 ??timer.Reset(10?*?time.Second) ?}() ?//?主線程繼續(xù)執(zhí)行其他任務(wù) ?//?... ?//?阻塞主線程,保持程序運(yùn)行 ?select?{} }
4.并發(fā)任務(wù)協(xié)作的場(chǎng)景
在某些情況下,你可能需要多個(gè)Goroutine之間進(jìn)行協(xié)作和同步。例如,一個(gè)Goroutine負(fù)責(zé)生產(chǎn)任務(wù),而另一個(gè)Goroutine負(fù)責(zé)消費(fèi)任務(wù)并進(jìn)行處理。你可以使用通道來(lái)實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型。生產(chǎn)者將任務(wù)發(fā)送到通道中,而消費(fèi)者從通道中接收任務(wù)并進(jìn)行處理。通過(guò)這種方式,可以實(shí)現(xiàn)任務(wù)的有效分配和協(xié)同處理。
package?main import?( ?"fmt" ?"time" ) //?任務(wù)結(jié)構(gòu)體 type?Task?struct?{ ?ID???int ?Data?string ?//?其他任務(wù)相關(guān)的字段 } //?生產(chǎn)者,負(fù)責(zé)生產(chǎn)任務(wù)并發(fā)送到通道 func?producer(ch?chan<-?Task)?{ ?for?i?:=?1;?i?<=?10;?i++?{ ??task?:=?Task{ ???ID:???i, ???Data:?fmt.Sprintf("任務(wù)?%d",?i), ???//?設(shè)置其他任務(wù)字段 ??} ??ch?<-?task ??fmt.Println("生產(chǎn)任務(wù):",?task.Data) ??time.Sleep(500?*?time.Millisecond)?//?模擬生產(chǎn)任務(wù)的耗時(shí) ?} ?close(ch)?//?關(guān)閉通道,表示所有任務(wù)都已生產(chǎn)完畢 } //?消費(fèi)者,負(fù)責(zé)從通道接收任務(wù)并進(jìn)行處理 func?consumer(ch?<-chan?Task)?{ ?for?task?:=?range?ch?{ ??fmt.Println("消費(fèi)任務(wù):",?task.Data) ??//?執(zhí)行任務(wù)的處理邏輯 ??time.Sleep(1?*?time.Second)?//?模擬處理任務(wù)的耗時(shí) ?} } func?main()?{ ?//?創(chuàng)建一個(gè)任務(wù)通道,用于生產(chǎn)者和消費(fèi)者之間的通信 ?taskChannel?:=?make(chan?Task) ?//?啟動(dòng)生產(chǎn)者Goroutine ?go?producer(taskChannel) ?//?啟動(dòng)多個(gè)消費(fèi)者Goroutine ?for?i?:=?1;?i?<=?3;?i++?{ ??go?consumer(taskChannel) ?} ?//?阻塞主線程,保持程序運(yùn)行 ?select?{} }
在上面的代碼種,使用了定時(shí)器(time.Timer)結(jié)合Goroutine來(lái)實(shí)現(xiàn)異步任務(wù)調(diào)度。通過(guò)啟動(dòng)一個(gè)Goroutine來(lái)等待定時(shí)器的觸發(fā)事件,并執(zhí)行相應(yīng)的任務(wù)。這樣可以在后臺(tái)自動(dòng)執(zhí)行任務(wù),而不需要阻塞主線程。
到此這篇關(guān)于4個(gè)場(chǎng)景教會(huì)你Go中Goroutine和通道是怎么用的的文章就介紹到這了,更多相關(guān)Go Goroutine通道內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang實(shí)現(xiàn)java uuid的序列化方法
這篇文章主要介紹了golang實(shí)現(xiàn)java uuid的序列化方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09CSP communicating sequential processes并發(fā)模型
這篇文章主要為大家介紹了CSP communicating sequential processes并發(fā)模型,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Go語(yǔ)言異常處理error、panic、recover的使用
GO語(yǔ)言中引入的異常的處理方式為error、panic、recover ,本文主要介紹了Go語(yǔ)言異常處理error、panic、recover的使用,感興趣的可以了解一下2024-08-08go語(yǔ)言實(shí)現(xiàn)銀行卡號(hào)Luhn校驗(yàn)
這篇文章主要為大家介紹了go語(yǔ)言Luhn校驗(yàn)測(cè)試銀行卡號(hào)碼的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Go Asynq異步任務(wù)處理的實(shí)現(xiàn)
Asynq是一個(gè)新興的異步任務(wù)處理解決方案,它提供了輕量級(jí)的、易于使用的API,本文主要介紹了Go Asynq異步任務(wù)處理的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-06-06go?singleflight緩存雪崩源碼分析與應(yīng)用
這篇文章主要為大家介紹了go?singleflight緩存雪崩源碼分析與應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09