golang Goroutine超时控制

这篇具有很好参考价值的文章主要介绍了golang Goroutine超时控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.个人理解 

package main

import (
	"context"
	"fmt"
	"runtime"
	"time"
)

func main() {
	// 为了方便查看设置的计数器
	//go func() {
	//	var o int64
	//	for {
	//		o++
	//		fmt.Println(o)
	//		time.Sleep(time.Second)
	//	}
	//}()

	// 开启协程
	for i := 0; i < 100; i++ {

		go func(i int) {
			// 利用context 设置超时上下文
			ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
			// 主动退出信号
			endDone := make(chan struct{})

			// 再次开启子协程异步处理业务逻辑
			go func() {
				select {
				// 监听是否超时
				case <-ctx.Done():
					fmt.Println("Goroutine timeout")
					return
				// 处理业务逻辑
				default:
					if i == 1 {
						time.Sleep(10 * time.Second)
					}

					// 此处代码会继续执行
					//fmt.Println("代码逻辑继续执行")

					// 主动退出
					close(endDone)
                    
                    return
				}
			}()

			// 监听父协程状态
			select {
			// 超时退出父协程,这里需要注意此时如果子协程已经执行并超时,子协程会继续执行中直到关闭,这块需要关注下。比如:查看数据确定数据是否已被修改等。
			case <-ctx.Done():
				fmt.Println("超时退出", i)
				cancel()
				return
			// 主动关闭
			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实现方式

package main

import (
	"context"
	"fmt"
	"runtime/debug"
	"strings"
	"time"
)

var (
	// ErrCanceled是取消上下文时返回的错误。
	ErrCanceled = context.Canceled
	// ErrTimeout是当上下文的截止日期过去时返回的错误。
	ErrTimeout = context.DeadlineExceeded
)

// DoOption定义了自定义DoWithTimeout调用的方法。
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()

	// 创建缓冲区大小为1的通道以避免goroutine泄漏
	done := make(chan error, 1)
	panicChan := make(chan interface{}, 1)
	go func() {
		defer func() {
			if p := recover(); p != nil {
				// 附加调用堆栈以避免在不同的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调用。
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

 一文搞懂 Go 超时控制_51CTO博客_go 超时处理文章来源地址https://www.toymoban.com/news/detail-704757.html

到了这里,关于golang Goroutine超时控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GO语言网络编程(并发编程)并发介绍,Goroutine

    进程和线程 并发和并行 协程和线程 协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。 线程:一个线程上可以跑多个协程,协程是轻量级的线程。 goroutine 只是由官方实现的超级\\\"线程池\\\"。 每个

    2024年02月09日
    浏览(35)
  • 【Golang】go编程语言适合哪些项目开发?

    前言 在当今数字化时代,软件开发已成为各行各业的核心需求之一。 而选择适合的编程语言对于项目的成功开发至关重要。 本文将重点探讨Go编程语言适合哪些项目开发,以帮助读者在选择合适的编程语言时做出明智的决策。 Go 编程语言适合哪些项目开发? Go是由Google开发

    2024年02月04日
    浏览(60)
  • 【golang】Context超时控制与原理

    在Go语言圈子中流行着一句话: Never start a goroutine without knowing how it will stop。 翻译:如果你不知道协程如何退出,就不要使用它。 在创建协程时,我们可能还会再创建一些别的子协程,那么这些协程的退出就成了问题。在Go1.7之后,Go官方引入了Context来实现协程的退出。不仅

    2024年01月22日
    浏览(26)
  • 【Golang】VsCode下开发Go语言的环境配置(超详细图文详解)

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏、个人专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 ​ 话说在前,Go语言的编码方式是 UTF-8 ,理论上你直接使用文本进行编辑也是可以的,当然为了提升我们的开发效率我们还是需

    2024年02月07日
    浏览(70)
  • Go 里的超时控制

    日常开发中我们大概率会遇到超时控制的场景,比如一个批量耗时任务、网络请求等;一个良好的超时控制可以有效的避免一些问题(比如 goroutine 泄露、资源不释放等)。 在 go 中实现超时控制的方法非常简单,首先第一种方案是 Time.After(d Duration): output: time.After() 会返回一

    2024年02月07日
    浏览(32)
  • golang学习-goroutine

    1、goroutine协程 goroutine 是 Go 语言支持并发的核心,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。区别于操作系统线程由系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度。例如Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类

    2024年01月18日
    浏览(26)
  • golang协程goroutine教程

    项目经常遇到一些批量任务执行太慢,需要开启多线程去处理,记录下在 Golang 中协程使用的一些操作。 协程是计算机程序的一类组件,推广了协作式多任务的子例程,允许执行被挂起与被恢复。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。

    2024年02月02日
    浏览(29)
  • Golang中的协程(goroutine)

    目录 进程 线程 并发 并行 协程(goroutine)  使用sync.WaitGroup等待协程执行完毕 多协程和多线程         进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程

    2024年02月05日
    浏览(29)
  • 深入理解 Golang: Goroutine 协程

    进程用来分配内存空间,是操作系统分配资源的最小单位;线程用来分配 CPU 时间,多个线程共享内存空间,是操作系统或 CPU 调度的最小单位;协程用来精细利用线程。协程就是将一段程序的运行状态打包,可以在线程之间调度。或者说将一段生产流程打包,使流程不固定在

    2024年02月11日
    浏览(70)
  • Golang goroutine MPG 模式浅析

    快速入门小结: (1) 主线程是一个物理线程,直接作用在cpu上的 。是重量级的,非常耗费cpu资源。 (2)协程从主线程开局的,是轻量级的线程,是逻辑态,对资源消耗相对小。 (3)Golang的协程机制是重要的特点,可以轻松的开启上万个协程。其它编程语言的并发机制是

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包