Golang優(yōu)雅保持main函數(shù)不退出的辦法
高能預(yù)警
本文包含演示部分,請讀者自行copy代碼編譯體驗。
參考資料:sync.WaitGroup / signal.Notify / context.CancelFunc
正文
我們有時會希望我們的程序保持執(zhí)行,但是有一種情況是:我們的代碼全部塞入go routine時,主函數(shù)會立刻退出,本文將和大家分享如何讓main函數(shù)優(yōu)雅地保持執(zhí)行。
問題演示:
func main() {
go func() {
for i := 0; i<10000;i ++ {
fmt.Println(i)
}
}()
}
此時我們可以看到,控制臺幾乎不會輸出任何內(nèi)容。究其原因,是主函數(shù)在go routine執(zhí)行前就已經(jīng)結(jié)束,也就是說go routine不會阻塞主函數(shù)。
可能有些讀者會想到,我直接加個死循環(huán)在下面,讓主函數(shù)不退出不就行啦?博主表示十分贊同,因為博主就是采用這個方法,導(dǎo)致服務(wù)器跑滿CPU從而不停的告警。
那么解決辦法是:讓死循環(huán)慢一點執(zhí)行,即添加以下內(nèi)容:
for {
time.Sleep(time.Second)
}
但是在博主的完美主義光環(huán)加持下,還是希望我們的代碼能更加優(yōu)雅,下面將介紹另外三種比較優(yōu)雅的保持main函數(shù)的辦法。
解決辦法演示
操作系統(tǒng)信號阻塞
先上代碼:
func main() {
c := make(chan os.Signal)
signal.Notify(c)
go func() {
fmt.Println("Go routine running")
time.Sleep(3*time.Second)
fmt.Println("Go routine done")
}()
<-c
fmt.Println("bye")
}
官網(wǎng)機翻:signal.Notify()方法使信號將傳入c。如果沒有提供信號,所有傳入的信號將被中繼到c。
- 這里我們創(chuàng)建了一個os.Signal類型的管道。當(dāng)管道為空的時候,讀管道操作“<-”會阻塞住,直到我們向進程發(fā)送一個信號(例如 Ctrl+C),才會繼續(xù)執(zhí)行該操作后面的代碼。
上下文操作阻塞
再上代碼:
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
fmt.Println("Go routine running")
time.Sleep(3 * time.Second)
fmt.Println("Go routine done")
cancel()
}()
<-ctx.Done()
fmt.Println("bye")
}
官網(wǎng)機翻:CancelFunc() 通知操作放棄其(當(dāng)前的)工作。CancelFunc() 不會等待工作停止。
- 這也是一個十分優(yōu)雅的辦法,我們創(chuàng)建一個可以終止的上下文——context.WithCancel(),并在go routine執(zhí)行完畢時調(diào)用其返回的CancelFunc() 方法,即表示該上下文已經(jīng)結(jié)束了。而在這之前,我們會使用<-ctx.Done()來一直等待上下文的結(jié)束,也就是說main函數(shù)被成功阻塞,并等待go routine執(zhí)行完畢并執(zhí)行了cancel()方法后優(yōu)雅退出。
WaitGroup阻塞
然后上代碼:
func main() {
wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
time.Sleep(3*time.Second)
fmt.Println("3 second passed")
wg.Done()
}()
go func() {
time.Sleep(5*time.Second)
fmt.Println("5 second passed")
wg.Done()
}()
wg.Wait()
fmt.Println("bye")
}官網(wǎng)機翻:WaitGroup 等待一組 go routine 完成。主 go routine 調(diào)用 Add() 來設(shè)置要等待的 go routine 的數(shù)量。
- 我們首先創(chuàng)建一個WaitGroup{}對象,然后調(diào)用Add()方法,在里面?zhèn)魅胛覀兘酉聛頃?chuàng)建的go routine的數(shù)量,每當(dāng)我們執(zhí)行完一個go routine時,調(diào)用一次Done()方法,使得正執(zhí)行的go routine的數(shù)量減一,當(dāng)減到0時,Wait()方法將不再等待(阻塞),使main函數(shù)繼續(xù)向下執(zhí)行。
小結(jié)
以上就是我們告別for {}或者select {},并優(yōu)雅地阻塞主函數(shù)的三種辦法,也是博主作為新手時對Go語言特性的入門級體驗。
總結(jié)
到此這篇關(guān)于Golang優(yōu)雅保持main函數(shù)不退出的文章就介紹到這了,更多相關(guān)Golang main函數(shù)不退出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Golang?Validator包實現(xiàn)數(shù)據(jù)驗證詳解
在開發(fā)過程中,數(shù)據(jù)驗證是一個非常重要的環(huán)節(jié),而golang中的Validator包是一個非常常用和強大的數(shù)據(jù)驗證工具,提供了簡單易用的API和豐富的驗證規(guī)則,下面我們就來看看Validator包的具體使用吧2023-12-12
Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別
這篇文章主要介紹了Go處理大數(shù)組使用for?range和for循環(huán)的區(qū)別,對于遍歷大數(shù)組而言,for循環(huán)能比for?range循環(huán)更高效與穩(wěn)定,這一點在數(shù)組元素為結(jié)構(gòu)體類型更加明顯,下文具體分析感興趣得小伙伴可以參考一下2022-05-05
golang給函數(shù)參數(shù)設(shè)置默認值的幾種方式小結(jié)(函數(shù)參數(shù)默認值
在日常開發(fā)中我們有時候需要使用默認設(shè)置,下面這篇文章主要給大家介紹了關(guān)于golang給函數(shù)參數(shù)設(shè)置默認值的幾種方式小結(jié)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-01-01
Go開發(fā)go-optioner工具實現(xiàn)輕松生成函數(shù)選項模式代碼
go-optioner?是一個在?Go?代碼中生成函數(shù)選項模式代碼的工具,可以根據(jù)給定的結(jié)構(gòu)定義自動生成相應(yīng)的選項代碼,下面就來聊聊go-optioner是如何使用的吧2023-07-07
解決Go中攔截HTTP流數(shù)據(jù)時字段丟失的問題
在開發(fā)高并發(fā)的Web應(yīng)用時,尤其是在處理HTTP代理和流數(shù)據(jù)攔截的場景下,遇到數(shù)據(jù)丟失的問題并不罕見,最近,在一個項目中,我遇到了一個棘手的問題:在攔截并轉(zhuǎn)發(fā)HTTP流數(shù)據(jù)的過程中,某些數(shù)據(jù)字段因為處理過快而被丟失,所以本文給大家介紹如何解決這個問題2024-08-08
基于go實例網(wǎng)絡(luò)存儲協(xié)議詳解
這篇文章主要為大家介紹了基于go實例網(wǎng)絡(luò)存儲協(xié)議詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Go語言中Gin框架使用JWT實現(xiàn)登錄認證的方案
在如今前后端分離開發(fā)的大環(huán)境中,我們需要解決一些登陸,后期身份認證以及鑒權(quán)相關(guān)的事情,通常的方案就是采用請求頭攜帶token的方式進行實現(xiàn),本文給大家介紹了Go語言中Gin框架使用JWT實現(xiàn)登錄認證的方案,需要的朋友可以參考下2024-11-11

