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