1.個(gè)人理解?
package main
import (
"context"
"fmt"
"runtime"
"time"
)
func main() {
// 為了方便查看設(shè)置的計(jì)數(shù)器
//go func() {
// var o int64
// for {
// o++
// fmt.Println(o)
// time.Sleep(time.Second)
// }
//}()
// 開(kāi)啟協(xié)程
for i := 0; i < 100; i++ {
go func(i int) {
// 利用context 設(shè)置超時(shí)上下文
ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
// 主動(dòng)退出信號(hào)
endDone := make(chan struct{})
// 再次開(kāi)啟子協(xié)程異步處理業(yè)務(wù)邏輯
go func() {
select {
// 監(jiān)聽(tīng)是否超時(shí)
case <-ctx.Done():
fmt.Println("Goroutine timeout")
return
// 處理業(yè)務(wù)邏輯
default:
if i == 1 {
time.Sleep(10 * time.Second)
}
// 此處代碼會(huì)繼續(xù)執(zhí)行
//fmt.Println("代碼邏輯繼續(xù)執(zhí)行")
// 主動(dòng)退出
close(endDone)
return
}
}()
// 監(jiān)聽(tīng)父協(xié)程狀態(tài)
select {
// 超時(shí)退出父協(xié)程,這里需要注意此時(shí)如果子協(xié)程已經(jīng)執(zhí)行并超時(shí),子協(xié)程會(huì)繼續(xù)執(zhí)行中直到關(guān)閉,這塊需要關(guān)注下。比如:查看數(shù)據(jù)確定數(shù)據(jù)是否已被修改等。
case <-ctx.Done():
fmt.Println("超時(shí)退出", i)
cancel()
return
// 主動(dòng)關(guān)閉
case <-endDone:
fmt.Println("主動(dòng)退出", i)
cancel()
return
}
}(i)
}
//time.Sleep(8 * time.Second)
time.Sleep(12 * time.Second)
// 查看當(dāng)前還存在多少運(yùn)行中的goroutine
fmt.Println("number of goroutines:", runtime.NumGoroutine())
}
2.go-zero實(shí)現(xiàn)方式
package main
import (
"context"
"fmt"
"runtime/debug"
"strings"
"time"
)
var (
// ErrCanceled是取消上下文時(shí)返回的錯(cuò)誤。
ErrCanceled = context.Canceled
// ErrTimeout是當(dāng)上下文的截止日期過(guò)去時(shí)返回的錯(cuò)誤。
ErrTimeout = context.DeadlineExceeded
)
// DoOption定義了自定義DoWithTimeout調(diào)用的方法。
type DoOption func() context.Context
// DoWithTimeout運(yùn)行帶有超時(shí)控制的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)
}
參考文獻(xiàn):
?https://github.com/zeromicro/go-zero/blob/master/core/fx/timeout.go文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-704757.html
?一文搞懂 Go 超時(shí)控制_51CTO博客_go 超時(shí)處理文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-704757.html
到了這里,關(guān)于golang Goroutine超時(shí)控制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!