golang Goroutine超時控制的實現(xiàn)
更新時間:2023年09月14日 10:10:26 作者:一個搬磚的程序猿
日常開發(fā)中我們大概率會遇到超時控制的場景,比如一個批量耗時任務(wù)、網(wǎng)絡(luò)請求等,本文主要介紹了golang Goroutine超時控制的實現(xiàn),文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
1.個人理解
package main
import (
"context"
"fmt"
"runtime"
"time"
)
func main() {
// 為了方便查看設(shè)置的計數(shù)器
//go func() {
// var o int64
// for {
// o++
// fmt.Println(o)
// time.Sleep(time.Second)
// }
//}()
// 開啟協(xié)程
for i := 0; i < 100; i++ {
go func(i int) {
// 利用context 設(shè)置超時上下文
ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
// 主動退出信號
endDone := make(chan struct{})
// 再次開啟子協(xié)程異步處理業(yè)務(wù)邏輯
go func() {
select {
// 監(jiān)聽是否超時
case <-ctx.Done():
fmt.Println("Goroutine timeout")
return
// 處理業(yè)務(wù)邏輯
default:
if i == 1 {
time.Sleep(10 * time.Second)
}
// 此處代碼會繼續(xù)執(zhí)行
//fmt.Println("代碼邏輯繼續(xù)執(zhí)行")
// 主動退出
close(endDone)
return
}
}()
// 監(jiān)聽父協(xié)程狀態(tài)
select {
// 超時退出父協(xié)程,這里需要注意此時如果子協(xié)程已經(jīng)執(zhí)行并超時,子協(xié)程會繼續(xù)執(zhí)行中直到關(guān)閉,這塊需要關(guān)注下。比如:查看數(shù)據(jù)確定數(shù)據(jù)是否已被修改等。
case <-ctx.Done():
fmt.Println("超時退出", i)
cancel()
return
// 主動關(guān)閉
case <-endDone:
fmt.Println("主動退出", i)
cancel()
return
}
}(i)
}
//time.Sleep(8 * time.Second)
time.Sleep(12 * time.Second)
// 查看當前還存在多少運行中的goroutine
fmt.Println("number of goroutines:", runtime.NumGoroutine())
}2.go-zero實現(xiàn)方式
package main
import (
"context"
"fmt"
"runtime/debug"
"strings"
"time"
)
var (
// ErrCanceled是取消上下文時返回的錯誤。
ErrCanceled = context.Canceled
// ErrTimeout是當上下文的截止日期過去時返回的錯誤。
ErrTimeout = context.DeadlineExceeded
)
// DoOption定義了自定義DoWithTimeout調(diào)用的方法。
type DoOption func() context.Context
// DoWithTimeout運行帶有超時控制的fn。
func DoWithTimeout(fn func() error, timeout time.Duration, opts ...DoOption) error {
parentCtx := context.Background()
for _, opt := range opts {
parentCtx = opt()
}
ctx, cancel := context.WithTimeout(parentCtx, timeout)
defer cancel()
// 創(chuàng)建緩沖區(qū)大小為1的通道以避免goroutine泄漏
done := make(chan error, 1)
panicChan := make(chan interface{}, 1)
go func() {
defer func() {
if p := recover(); p != nil {
// 附加調(diào)用堆棧以避免在不同的goroutine中丟失
panicChan <- fmt.Sprintf("%+v\n\n%s", p, strings.TrimSpace(string(debug.Stack())))
}
}()
done <- fn()
}()
select {
case p := <-panicChan:
panic(p)
case err := <-done:
return err
case <-ctx.Done():
return ctx.Err()
}
}
// WithContext使用給定的ctx自定義DoWithTimeout調(diào)用。
func WithContext(ctx context.Context) DoOption {
return func() context.Context {
return ctx
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
fmt.Println(1111)
time.Sleep(time.Second * 5)
fmt.Println(2222)
cancel()
}()
err := DoWithTimeout(func() error {
fmt.Println("aaaa")
time.Sleep(10 * time.Second)
fmt.Println("bbbb")
return nil
}, 3*time.Second, WithContext(ctx))
fmt.Println(err)
time.Sleep(15 * time.Second)
//err := DoWithTimeout(func() error {
// fmt.Println(111)
// time.Sleep(time.Second * 3)
// fmt.Println(222)
// return nil
//}, time.Second*2)
//
//fmt.Println(err)
//time.Sleep(6 * time.Second)
//
//fmt.Println("number of goroutines:", runtime.NumGoroutine())
}package fx
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestWithPanic(t *testing.T) {
assert.Panics(t, func() {
_ = DoWithTimeout(func() error {
panic("hello")
}, time.Millisecond*50)
})
}
func TestWithTimeout(t *testing.T) {
assert.Equal(t, ErrTimeout, DoWithTimeout(func() error {
time.Sleep(time.Millisecond * 50)
return nil
}, time.Millisecond))
}
func TestWithoutTimeout(t *testing.T) {
assert.Nil(t, DoWithTimeout(func() error {
return nil
}, time.Millisecond*50))
}
func TestWithCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(time.Millisecond * 10)
cancel()
}()
err := DoWithTimeout(func() error {
time.Sleep(time.Minute)
return nil
}, time.Second, WithContext(ctx))
assert.Equal(t, ErrCanceled, err)
}參考文獻:
https://github.com/zeromicro/go-zero/blob/master/core/fx/timeout.go
到此這篇關(guān)于golang Goroutine超時控制的實現(xiàn)的文章就介紹到這了,更多相關(guān)go Goroutine超時控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
gin正確多次讀取http?request?body內(nèi)容實現(xiàn)詳解
這篇文章主要為大家介紹了gin正確多次讀取http?request?body內(nèi)容實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01
Go語言中三個輸入函數(shù)(scanf,scan,scanln)的區(qū)別解析
本文詳細介紹了Go語言中三個輸入函數(shù)Scanf、Scan和Scanln的區(qū)別,包括用法、功能和輸入終止條件等,本文給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-10-10

