在Go语言中,将sync.WaitGroup存储在Context中是否是一个好主意?

huangapple go评论72阅读模式
英文:

Is it a good idea to store a sync.WaitGroup inside a Context in go?

问题

我需要等待一些嵌套的go协程调用完成,目前正在通过调用栈传递一个sync.WaitGroup。

由于每个签名中已经有一个上下文,我考虑将等待组添加到上下文中。

可能看起来像这样:

func TestWaitGroupInContext(t *testing.T) {
    res := make(chan int, 3)

    sendNumber := func(ctx context.Context, i int) {
        wg := ctx.Value("wg").(*sync.WaitGroup)
        defer wg.Done()
        time.Sleep(20 * time.Millisecond)
        res <- i
    }

    doStuff := func(ctx context.Context) {
        wg := ctx.Value("wg").(*sync.WaitGroup)

        wg.Add(3)
        go sendNumber(ctx, 1)
        go sendNumber(ctx, 2)
        go sendNumber(ctx, 3)

    }

    wg := sync.WaitGroup{}
    ctx := context.Background()
    ctx = context.WithValue(ctx, "wg", &wg)
    doStuff(ctx)

    assert.Equal(t, 0, len(res))
    wg.Wait()
    assert.Equal(t, 3, len(res))

}

你认为这样做可能有什么问题吗?

英文:

I need to wait for some nested go go routine calls to finish and currently passing a sync.WaitGroup along the call stack.

Since I already have a context in every signature I'm thinking about just adding the wait group to the context.

Could look like this:

func TestWaitGroupInContext(t *testing.T) {
	res := make(chan int, 3)

	sendNumber := func(ctx context.Context, i int) {
		wg := ctx.Value(&quot;wg&quot;).(*sync.WaitGroup)
		defer wg.Done()
		time.Sleep(20 * time.Millisecond)
		res &lt;- i
	}

	doStuff := func(ctx context.Context) {
		wg := ctx.Value(&quot;wg&quot;).(*sync.WaitGroup)

		wg.Add(3)
		go sendNumber(ctx, 1)
		go sendNumber(ctx, 2)
		go sendNumber(ctx, 3)

	}

	wg := sync.WaitGroup{}
	ctx := context.Background()
	ctx = context.WithValue(ctx, &quot;wg&quot;, &amp;wg)
	doStuff(ctx)

	assert.Equal(t, 0, len(res))
	wg.Wait()
	assert.Equal(t, 3, len(res))

}

Any thoughts why this could be a bad idea?

答案1

得分: 2

上下文(Context)用于存储请求特定的信息。它不适合存储像等待组(waitgroup)这样的内容。但是,也没有规定禁止这样做。

在你给出的示例中,你可以在封闭的作用域中简单地声明等待组,使其在所有goroutine中都可用,而无需将其作为单独的参数传递。

通常情况下,你不需要将等待组作为参数传递给goroutine。你可以这样做:

wg := sync.WaitGroup{}
ctx := context.Background()

wg.Add(1)
go func() {
   defer wg.Done()
   doStuff(ctx)
}()

wg.Add(1)
go func() {
   defer wg.Done()
   doOtherStuff(ctx)
}()

如果像这样无法传递等待组,最好明确地将其作为参数传递。

英文:

Context is to store request-specific information. It is not a good place to store something like a waitgroup. However, there are no rules against it either.

In the example you gave, you can simply declare the waitgroup in the enclosing scope, and make it available to all goroutines without passing it as a separate argument.

Generally, you don't have to pass the waitgroup to the goroutines as arguments. You can always do:

wg:=sync.WaitGroup{}
ctx:=context.Background()

wg.Add(1)
go func() {
   defer wg.Done()
   doStuff(ctx)
}()
wg.Add(1)
go func() {
   defer wg.Done()
   doOtherStuff(ctx)
}()

If passing a waitgroup is not possible like this, it is best to pass it explicitly.

huangapple
  • 本文由 发表于 2022年12月13日 04:17:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/74776842.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定