Skip to content

如何判断当前调用是否是 Goroutine

标签
开发/语言/Golang
开发/并发
开发/并发/协程
开发/语言/Golang/Goroutine
字数
555 字
阅读时间
3 分钟

TL;DR

关键在于使用 Golang 提供的 runtime 包的 runtime.NumGoroutine() 函数。

代码示例

创建一个工具包,该文件命名为 util.go

go
package util

var disableGoroutine bool // 创建一个不导出的变量

// SetDisableGoroutine 设定为「不开启协程」,我们通过这个函数动态设定是否走协程
func SetDisableGoroutine(set bool) {
	disableGoroutine = set
}

// Go 我们创建一个嵌套的 Go 方法来控制 Goroutine 的开关与否
func Go(f func()) {
  // 嵌套一个内层函数
	internal := func() {
		defer func() {
		  // 从 panic(恐慌)中还原
			if p := recover(); p != nil {
				// From net/http/server.go
				// https://github.com/golang/go/blob/release-branch.go1.13/src/net/http/server.go#L1765
				const size = 64 << 10
				buf := make([]byte, size)
				// if the size of stack tracing message is larger than 64k, the message will be truncated.
				buf = buf[:runtime.Stack(buf, false)] // 获取调用栈
				log.Errorf("Panic recovered, %v\n%s", p, buf) // 输出日志
			}
		}()
		f() // 传入的需要进行协程的函数 f
	}

	// 实际控制开关与否的逻辑
	if disableGoroutine {
		internal()
	} else {
		go internal()
	}
}

创建工具包对应的测试文件 util_test.go

go
package util

// 测试函数
func TestSetDisableGoroutine(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	SetDisableGoroutine(true) // 设定为 true
	assert.True(disableGoroutine) // 断言

	SetDisableGoroutine(false) // 设定为 false
	assert.False(disableGoroutine) // 断言

	var wg sync.WaitGroup // 创建 waitgroup 来等待协程结束
	var afterGoroutineCount int // 协程 go 出去之后的 Goroutine 计数
	aquireCurrentGoroutineID := func() {
	    afterGoroutineCount = runtime.NumGoroutine() // 使用 runtime.NumGoroutine() 获取 Go 协程总数
		wg.Done() // 标记 waitgroup 为完成
	}

	// 打开 goroutine 的情况
	SetDisableGoroutine(false)
	require.False(disableGoroutine) // 断言
	beforeGoroutineCount := runtime.NumGoroutine() // 协程 go 出去之前的 Goroutine 计数
	wg.Add(1) // Go 出去之前给 waitgroup 加一
	Go(aquireCurrentGoroutineID) // 执行重新封装的 Go 协程函数
	wg.Wait() // 等待协程结束,确保我们的 afterGoroutineCount 变量被更新
	assert.Equal(beforeGoroutineCount+1, afterGoroutineCount) // 可以断言 afterGoroutineCount 的值比 beforeGoroutineCount 大,相差 1

	// 关闭 goroutine 的情况
	SetDisableGoroutine(true)
	require.True(disableGoroutine) // 断言
	beforeGoroutineCount = runtime.NumGoroutine() // 协程 go 出去之前的 Goroutine 计数
	wg.Add(1) // Go 出去之前给 waitgroup 加一
	Go(aquireCurrentGoroutineID) // 执行重新封装的 Go 协程函数
	wg.Wait() // 等待协程结束,确保我们的 afterGoroutineCount 变量被更新
	assert.Equal(beforeGoroutineCount, afterGoroutineCount) // 可以断言此时没有变化,Go 函数的调用并没有产生新的协程
}

贡献者

页面历史

撰写