<p>I know this can be done in your own code with a counter variable, etc; But it'd be nice to see some sort of a limit in-built into <code>sync.WaitGroup</code>.</p>
<p>For example;</p>
<pre><code>var wg sync.WaitGroup
wg.Limit(1)
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("start #1")
time.Sleep(time.Second * 3)
fmt.Println("done #1")
}()
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("start #2")
time.Sleep(time.Second)
fmt.Println("done #2")
}()
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("start #3")
time.Sleep(time.Second * 1)
fmt.Println("done #3")
}()
wg.Wait()
</code></pre>
<p>Each time <code>wg.Add()</code> is called, and the limit is reached, it will pause and wait for the waitgroup counter to decrease (i.e <code>wg.Done()</code> is called).</p>
<p>Thoughts?</p>
<hr/>**评论:**<br/><br/>drvd: <pre><p>Are you trying to hammer a WaitGroup into a Semaphore hole? If yes: Why?</p></pre>Acidic92: <pre><p>I'm not sure what you mean :)</p></pre>qu33ksilver: <pre><p>A semaphore is what you actually want for your scenario. You can read up wikipedia for better explanations than if I attempt at it :)</p>
<p>A waitgroup is only useful when you want to wait unless a certain no. of goroutines have completed execution.</p>
<p>Using a buffered channel with blocking semantics is the standard way to implement semaphores in Go. As answered by <a href="/u/hobbified" rel="nofollow">/u/hobbified</a> here - <a href="https://www.reddit.com/r/golang/comments/70sfmk/syncwaitgroup_limit/dn5rmmz/" rel="nofollow">https://www.reddit.com/r/golang/comments/70sfmk/syncwaitgroup_limit/dn5rmmz/</a></p></pre>Acidic92: <pre><p>Ah I see, well actually when I thought that it'd be good if there was an in-built limit to wait groups, I wasn't trying anything specific; I just know in the past I've had to (sometimes) setup my own limit counter, and it just seems like it could be a few extra lines of code in the sync package which would help a lot of people if they need it.</p></pre>Sythe2o0: <pre><p>Internally changing wait groups to support that would probably result in worse performance for the standard use case, however, and there are already tools for your use case (or you can construct your use case with a wrapper struct). </p></pre>Acidic92: <pre><p>worse performance? Only performance decrease would come an if statement such as:</p>
<pre><code>if wg.count >= wg.limit {
</code></pre>
<p>Each time <code>wg.Add(1)</code> is called.</p></pre>skidooer: <pre><p>Often you will only call Add once. How does this handle that?</p>
<pre><code>wg.Limit(1)
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
// Do something.
wg.Done()
}()
}
</code></pre>
<p>Will Add block forever since it cannot be satisfied by the limit?</p></pre>Ploobers: <pre><p>This is our package that limits concurrency and helps you manage return errors, with similar semantics to a wait group. We've easily pushed trillions of goroutines through it, so it should be stable.
<a href="https://github.com/nozzle/throttler" rel="nofollow">https://github.com/nozzle/throttler</a></p></pre>hobbified: <pre><p>But then it wouldn't really be a waitgroup. You can make it yourself easily enough with a type that contains a waitgroup and a buffered channel of <code>struct {}</code> (used as a semaphore). It could implement the same interface as sync.WaitGroup except for the fact that its zero value wouldn't be useful so you would want to give it a constructor.</p>
<p>I think this is about right: <a href="https://play.golang.org/p/ChjP2wpvyt" rel="nofollow">https://play.golang.org/p/ChjP2wpvyt</a></p></pre>Acidic92: <pre><p>But then it wouldn't really be a waitgroup? How so?</p></pre>Morgahl: <pre><p>I've accomplished this in that past with a struct with an internal channel handing out tokens when <code>Add</code> is called.</p>
<p>An alternative and honestly cleaner pattern most times is a Worker Pool. </p></pre>epiris: <pre><p>This sounds like a job for sync.Cond, <a href="https://gist.github.com/cstockton/d611ced26bb6b4d3f7d4237abb8613c4" rel="nofollow">gist</a> and <a href="https://play.golang.org/p/haEHcprSwM" rel="nofollow">play</a>.</p></pre>whizack: <pre><p>what you probably want is something more like a separate struct that composes a sync.WaitGroup and a sync.Cond (so you can control the number of routines that wake) bound to a sync.Mutex while using sync/atomic to increment/decrement your current active count.</p>
<p>using a channel for the semaphore is sufficient for values of n that are small, but allocating n sentinel values into a channel when n is a large number would not be an efficient use of space.</p>
<p>The one major benefit you get with channels is that you can reason about time easier than you might using the other sync types.</p></pre>cs-guy: <pre><p>Using chan struct{} solves the space problem.</p></pre>whizack: <pre><p>i just read the cheney article about the empty struct, i wasn't aware struct{} consumed zero space. thanks.</p></pre>earthboundkid: <pre><p>I wrote a taskpool to solve that problem: <a href="https://godoc.org/github.com/carlmjohnson/monterey-jack/taskpool" rel="nofollow">https://godoc.org/github.com/carlmjohnson/monterey-jack/taskpool</a></p></pre>Kraigius: <pre><p>For simple usage I use something like this: <a href="https://play.golang.org/p/ziuuy02u18" rel="nofollow">https://play.golang.org/p/ziuuy02u18</a></p></pre>jamesrr39: <pre><p>I agree - most operations that can benefit from concurrency also have a practical limit; CPU-bound operations are limited by the number of CPUs, file-bound operations need to be limited by a OS limit on the number of open file descriptors, etc.</p>
<p>After running into this problem a few times and doing some reading-up about it I came up with this, which has a similar API to sync.WaitGroup. It would be nice to have something in the sync package instead, but it doesn't exist there (yet!). Hope it's helpful! <a href="https://github.com/jamesrr39/semaphore" rel="nofollow">https://github.com/jamesrr39/semaphore</a></p></pre>Acidic92: <pre><p>I'm happy someone agrees! Seems like something harmless that could just be there in case it's needed!
Also, implementing it would break no existing code at all.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传