Golang實(shí)現(xiàn)協(xié)程超時(shí)控制的方式總結(jié)
Golang對協(xié)程做超時(shí)的控制大概有兩種方式,timer 和context。對于并發(fā)情況,又有不同的方式。
我們知道,go協(xié)程如果不做好處理,很容易造成內(nèi)存泄漏。特別是在高并發(fā)的情況下,如果某一個(gè) goroutine 由于意外退出,則會(huì)導(dǎo)致接收方一直阻塞,從而掛起主程序。
對goroutine做超時(shí)控制,能夠有效避免這種情況發(fā)生。
Timer+Select
time.After 實(shí)現(xiàn)超時(shí)控制
- 利用 time.After 啟動(dòng)了一個(gè)異步的定時(shí)器,返回一個(gè) channel,當(dāng)超過指定的時(shí)間后,該 channel 將會(huì)接受到信號(hào)。
- 啟動(dòng)了子協(xié)程,函數(shù)執(zhí)行結(jié)束后,將向 channel ch 發(fā)送結(jié)束信號(hào)。
- 使用 select 阻塞等待 done 或 time.After 的信息,若超時(shí),輸出timeout,若沒有超時(shí),則輸出done。
func TestContext12(t *testing.T) { ch := make(chan struct{}, 1) go func() { fmt.Println("running...") time.Sleep(3 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("done") case <-time.After(2 * time.Second): fmt.Println("timeout") } }
output:
=== RUN TestContext12
running...
timeout
--- PASS: TestContext12 (3.01s)
PASS
這里要注意一個(gè)點(diǎn),就是你創(chuàng)建ch 的時(shí)候,必須是帶緩沖的。如果不帶,在并發(fā)的情況下會(huì)怎樣呢?
NewTimer
也可以新建一個(gè)NewTimer,timer.C返回也是一個(gè)channel
func TestContext5(t *testing.T) { timer := time.NewTimer(time.Duration(time.Millisecond * 900)) ch := make(chan struct{}, 1) go func() { fmt.Println("running...") time.Sleep(3 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("done") case <-timer.C: fmt.Println("timeout") } }
output:
=== RUN TestContext5
running...
timeout
--- PASS: TestContext5 (0.90s)
PASS
留一個(gè)坑
為什么不需要關(guān)閉ch 管道?什么時(shí)候關(guān)閉ch?
Context+Select
- 第二種方案是利用 context,context.WithTimeout。
- 它接受一個(gè)Context和一個(gè)超時(shí)時(shí)間作為參數(shù),返回一個(gè)子Context和一個(gè)取消函數(shù)CancelFunc。
- 取消函數(shù)CancelFunc將釋放子Context與之有關(guān)的資源,因此在子Context中的相關(guān)操作一旦完成,應(yīng)該立即調(diào)用取消函數(shù)CancelFunc。
func TestContext6(t *testing.T) { ch := make(chan string) timeout, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go func() { time.Sleep(time.Second * 3) ch <- "done" }() select { case res := <-ch: fmt.Println(res) case <-timeout.Done(): fmt.Println("timout", timeout.Err()) } }
- 利用 context.WithTimeou 返回啟動(dòng)了一個(gè)異步的定時(shí)器,返回一個(gè) channel,當(dāng)超過指定的時(shí)間后,該 channel 將會(huì)接受到信號(hào)。
- 啟動(dòng)了子協(xié)程,函數(shù)執(zhí)行結(jié)束后,將向 channel ch 發(fā)送結(jié)束信號(hào)。
- 使用 select 阻塞等待 done 或 time.Done的信息,若超時(shí),輸出timeout,若沒有超時(shí),則輸出done。
output:
=== RUN TestContext6
timout context deadline exceeded
--- PASS: TestContext6 (2.00s)
PASS
context+并發(fā)協(xié)程
并發(fā)協(xié)程使用WaitGroup阻塞主協(xié)程保證生產(chǎn)協(xié)程和消費(fèi)協(xié)程正常執(zhí)行完成。
func TestContext3(t *testing.T) { withTimeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*1) waitGroup := sync.WaitGroup{} waitGroup.Add(2) go func() { //協(xié)程1 time.Sleep(time.Second) fmt.Println("finished 1") waitGroup.Done() }() go func() { //協(xié)程2 time.Sleep(time.Second * 2) fmt.Println("finished 2") waitGroup.Done() }() go func() { select { case <-withTimeout.Done(): fmt.Println("timeout") return default: waitGroup.Wait() cancelFunc() fmt.Println("finished all") return //結(jié)束監(jiān)聽協(xié)程 } }() <-withTimeout.Done() }
- 開啟協(xié)程1,等待1秒輸出。協(xié)程2,等待2秒輸出。
- context.WithTimeout等待1秒后返回函數(shù)。
- 1秒后,協(xié)程一輸出。但是協(xié)程二沒輸出,主線程不等待了。直接返回。
output:
=== RUN TestContext3
finished 1
--- PASS: TestContext3 (1.01s)
PASS
這里多一個(gè)問題,留個(gè)坑!
goroutine如何超時(shí)控制并發(fā)輸出a,b,c并且按照a,b,c的順序輸出?
到此這篇關(guān)于Golang實(shí)現(xiàn)協(xié)程超時(shí)控制的方式總結(jié)的文章就介紹到這了,更多相關(guān)Golang協(xié)程超時(shí)控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Golang如何優(yōu)雅判斷interface是否為nil
這篇文章主要為大家詳細(xì)介紹了Golang如何優(yōu)雅判斷interface是否為nil的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解下2024-01-01一文搞懂Golang 時(shí)間和日期相關(guān)函數(shù)
這篇文章主要介紹了Golang 時(shí)間和日期相關(guān)函數(shù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12golang http 連接超時(shí)和傳輸超時(shí)的例子
今天小編就為大家分享一篇golang http 連接超時(shí)和傳輸超時(shí)的例子,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07Golang中Channel實(shí)戰(zhàn)技巧與一些說明
channel是Go語言內(nèi)建的first-class類型,也是Go語言與眾不同的特性之一,下面這篇文章主要給大家介紹了關(guān)于Golang中Channel實(shí)戰(zhàn)技巧與一些說明的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11簡單高效!Go語言封裝二級(jí)認(rèn)證功能實(shí)現(xiàn)
本文將介紹如何使用Go語言封裝二級(jí)認(rèn)證功能,實(shí)現(xiàn)簡單高效的用戶認(rèn)證流程,二級(jí)認(rèn)證是一種安全措施,要求用戶在登錄后進(jìn)行額外的身份驗(yàn)證,以提高賬戶安全性,2023-10-10go語言基礎(chǔ) seek光標(biāo)位置os包的使用
這篇文章主要介紹了go語言基礎(chǔ) seek光標(biāo)位置os包的使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-05-05