<p>I'm trying to implement a worker queue/pool that will run a specified number of workers in goroutines</p>
<p>There's a couple of articles that show a method of doing this, but I'd like to know if there's any other simpler ways of achieving the same result. Here are the links:</p>
<p><a href="http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/">http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/</a>
<a href="http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html">http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html</a></p>
<p>I was thinking with how unbuffered channels and goroutines work, it would be possible to just keep an unbuffered jobs channel that all workers can listen to, and other goroutines can add jobs to, being blocked until a worker is available.</p>
<p>Testing out a version of this method though, it seems some type of deadlock occurs when two goroutines are trying to write to the jobs channel.</p>
<p>Here's my code: <a href="http://play.golang.org/p/C3igLrmb0s">http://play.golang.org/p/C3igLrmb0s</a></p>
<p>If you change the code though so only 3 jobs are created in the initial function, then the goroutines all work no problem, and a new job is added whenever a worker is done.</p>
<p>Any ideas on what's causing this?</p>
<hr/>**评论:**<br/><br/>Frakturfreund: <pre><p>Instead of <code>go run</code>, try <code>go run -race</code> instead. Although the 3 job variant seems to run smoothly, it too has a data race.</p></pre>beeker1121: <pre><p>Thanks for the response. I tried running it with <code>-race</code>, but I don't think it's showing anything because it runs without error, it's just locked somehow.</p>
<p>It doesn't really make sense. I would think if one goroutine (say the initial jobs addition in the for loop) is blocked from sending to <code>manager.jobsChannel</code> by the other (the goroutine listening for <code>case <-done</code> from all workers), it should wait no problem until there's a chance to send. When a worker sends back on <code>w.done <- true</code> though, both goroutines to add a job seem to be called at the same time.</p>
<p>Here's an updated version showing if the job was added or not:</p>
<p><a href="http://play.golang.org/p/bF-fPuFuNA" rel="nofollow">http://play.golang.org/p/bF-fPuFuNA</a></p></pre>beeker1121: <pre><p>Think I might see it actually.</p>
<p>The goroutine watching for <code>case <-done</code> is trying to send, but is blocked. Since it's blocked at the send portion, it will never execute <code>case <-done</code> when the workers finish, so all goroutines will complete and just sit there.</p>
<p>Edit: Looks like that was it, spawning the job addition in its own goroutine fixed it. Working code: <a href="http://play.golang.org/p/sUmPKxcD-M">http://play.golang.org/p/sUmPKxcD-M</a></p>
<p>Edit again: better version <a href="http://play.golang.org/p/OMi3dS_8LM">http://play.golang.org/p/OMi3dS_8LM</a></p></pre>Sphax: <pre><p>Next time you have a problem with a blocked goroutine I highly recommend using the net/http/pprof tool. You can get a goroutine dump and see what every routine is doing.</p></pre>bmurphy1976: <pre><p>You can also kill -SIGQUIT the process and get a dump of all running goroutines. I've used that to find many a deadlock. Quicker and easier than wiring up pprof if you don't use it that often.</p></pre>Sphax: <pre><p>Damn, I didn't even know that ! Or maybe I knew and just forgot. This is way better than redeploying the app with pprof activated.</p></pre>beeker1121: <pre><p>Very good to know, thank you both!</p></pre>Fwippy: <pre><p>You don't need to use a <code>select</code> statement every time you're interfacing with a channel - only when you want to choose between multiple channel interactions. (I.e: You want to read from whatever channel has data first, or you want to discard values if there isn't space to send on the output channel, etc.)</p>
<p>Simple modification of your last link there: <a href="http://play.golang.org/p/HmylnLPDZ2" rel="nofollow">http://play.golang.org/p/HmylnLPDZ2</a></p></pre>beeker1121: <pre><p>That makes sense and is much more concise, thanks!</p></pre>beeker1121: <pre><p>Thanks for the replies everyone.</p>
<p>While this method works, I think I'm going to instead use the method shown in the original links.</p>
<p>Having each Worker subscribe to the Manager via a worker pool channel, which the Manager then finds a Job for in a new goroutine and sends back via the Worker's Job channel, is a better way I think. Just seems more logical, where Jobs are now specific to each worker, rather than just handing them out randomly.</p></pre>
![](https://static.golangjob.cn/static/img/banner.png)