Question: recommend any Go concurrency libraries on top of channels?

xuanbao · · 656 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>We see some higher level concurrency patterns appear in Go while using it&#39;s channel primitives, but have yet to find any sort of library that puts together some of these patterns into something more reusable that takes care of some the pitfalls you have to watch out for.</p> <ul> <li><a href="http://www.golangpatterns.info/">http://www.golangpatterns.info/</a> is nice but just describes things in the form of boilerplate and very incomplete. These talks are cover a lot of them: *&#34;Google I/O 2012 - Go Concurrency Patterns&#34; <a href="https://www.youtube.com/watch?v=f6kdp27TYZs">https://www.youtube.com/watch?v=f6kdp27TYZs</a></li> <li><p>&#34;Google I/O 2013 - Advanced Go Concurrency Patterns&#34; <a href="https://www.youtube.com/watch?v=QDDwwePbDtw">https://www.youtube.com/watch?v=QDDwwePbDtw</a> </p></li> <li><p><a href="https://github.com/avelino/awesome-go">https://github.com/avelino/awesome-go</a> doesn&#39;t seem to reference any libraries in this area.</p></li> </ul> <p>We have starting putting together some into an internal library of our own, but the general feeling is that the lack of generics makes this code harder to reuse because of all channels end up using interface{} and all callbacks return just error. </p> <p>Example patters expected in library would be:</p> <ul> <li>producer-consumer with it&#39;s variations (1 producer and multiple consumers, N to 1, N to M)</li> <li>parallel for-loop</li> <li>futures</li> <li>go routine recovery (I don&#39;t expect anyone to use &#34;go func(...)()&#34; directly often)</li> <li>monitoring/cancellation</li> <li>first-to-complete wins</li> <li>etc</li> </ul> <p>Edit: removed anger inducing section Edit 2: added example patterns</p> <hr/>**评论:**<br/><br/>jerf: <pre><p>Most of the concurrency stuff is implemented by implementing it directly. It&#39;s easy for a library to trade away unobvious stuff, such as type safety with <code>interface{}</code> as you&#39;ve seen. Libraries that try to &#34;manage&#34; concurrency can also end up accidentally throwing away aspects of the surprisingly powerful and complicated channel abstraction; one litmus test is to check whether the &#34;helpful&#34; library can&#39;t be used with the <code>select</code> Go keyword.</p> <p>Goroutine recovery is generally easy because it&#39;s easy to wrap up a <code>func()</code> without loss into a function that can wrap around the beginning and the end of the target function, as well as putting in a defer. I wrote <a href="https://github.com/thejerf/suture">suture</a> for a particular case (uses an interface-based variant but the principle is similar), but in general it&#39;s easy enough that, again, you&#39;ll often just inline it, or declare a standard in your project. I mean, you can theoretically provide</p> <pre><code>func RunGoroutine(g, before, after, recover func()) { go func () { defer recover() before() g() after() }() } </code></pre> <p>in a Github library, but uses of this function are more opaque than the code it&#39;s trying to help prevent you from writing. Within <em>your</em> project, you may be able to provide helpful stubs that are aware of your project, and I personally highly recommend this approach, but there&#39;s not much a generic library can do to help you here.</p> <p>Cancellation is a hard problem. It&#39;s easy for a thing like <a href="https://blog.golang.org/context">net/context</a> (which, despite the name, can easily be used when no network is involved) to provide a call you can use to cancel, the hard part is in properly checking for it in your code without sacrificing performance. You&#39;re basically back to a cooperative scheduling situation, because there&#39;s no way (for good reason) to &#34;interrupt&#34; a goroutine in progress.</p> <p>For others, do go ahead and do a search. I&#39;ve found it&#39;s more effective to search <a href="https://godoc.org/">godoc.org directly</a> than use Google. (I&#39;ve lost at least three days of work to that mistake.) However, when you find a package that claims to do what you want, make sure you crack open the juicy insides and have a look. You may discover that it&#39;s just as easy to just do the thing yourself. With no particular offense intended to my fellow Go programmers, ah, how to phrase this nicely, many of those libraries should, ah, be carefully examined before putting them in your code base.</p></pre>mvitorino: <pre><p>Thanks for the tip about godoc.org, hadn&#39;t thought of that and both google and github search haven&#39;t been much help. I understand your points, and as you said, often an internal library that in a specific context makes sense and collects a few small abstractions, serves this purpose well. Go routines and channels are very powerful and unfortunately in our experience the most assured way to completely break an application with very hard to find issues. Unlike any other area of the language they are very hard to master and it&#39;s surprising to see so little work available for something that is supposed to be one of the key selling points of the language. I can&#39;t help but thinking that the large majority of people developing in Go are probably doing so in very sequential way. </p></pre>drvd: <pre><p>Why would anyone want a &#34;high level package&#34; for something like &#34;first-to-complete wins&#34; which is just a single channel receive?</p></pre>mvitorino: <pre><p>Because you would want to cancel the remaining goroutines.</p></pre>mvitorino: <pre><p>Cancel is important not just to avoid the cost of redundant processing if possible, but also you want to ensure you only close the channel after the other goroutines return, so that you don&#39;t have any sends after.</p></pre>klauspost: <pre><p>You are talking about general stuff, without anything to pin it on, which to me at least makes it difficult to discuss.</p> <p>In my experience there isn&#39;t any &#34;one way&#34; of handling concurrency. In all the cases, there have been subtle differences, where a general solution would be suboptimal.</p> <p>Maybe if you put down some cases, where you feel a library solution would help, that would be a good basis for discussion/recommendations.</p></pre>mvitorino: <pre><p>I mentioned the patterns in the videos and site:</p> <ul> <li>producer-consumer with it&#39;s variations (1 producer and multiple consumers, N to 1, N to M)</li> <li>parallel for-loop </li> <li>futures </li> <li>go routine recovery (I don&#39;t expect anyone to use &#34;go func(...)()&#34; directly often</li> <li>monitoring/cancellation</li> <li>first-to-complete wins</li> <li>etc</li> </ul></pre>Testiclese: <pre><p>The language doesn&#39;t lend itself to high-level abstractions like that because of the lack of generics. Any higher-level library would have to pass &#34;interface{}&#34; around. This is why nobody has written an &#34;insert&#34; package for a slice or a type-safe map/filter/reduce/etc functional framework - what would be the parameter/return types? </p> <p>Go provides a the basic building blocks but then it&#39;s up to you to wire them together and, sadly, for now, boilerplate is unavoidable. </p> <p>If you are bent on avoiding boiler-plate and following DRY principles, prepare to either: </p> <ol> <li>Lose type safety and pass interface{} around</li> <li>Rely on reflection to do the compiler&#39;s job for you, but at runtime</li> </ol></pre>Fwippy: <pre><p>There&#39;s also: 3. Generate code for each type you want to work on.</p></pre>mvitorino: <pre><p>Code generation may be a half-solution for parametric structs. But for isolated parametric functions it is completely impractical.</p></pre>pusic007: <pre><p><a href="https://github.com/ivpusic/grpool" rel="nofollow">https://github.com/ivpusic/grpool</a></p></pre>mvitorino: <pre><blockquote> <p><a href="https://github.com/ivpusic/grpool" rel="nofollow">https://github.com/ivpusic/grpool</a></p> </blockquote> <p>It&#39;s quite tiny. As an immediate reaction I feel that having an Arg member of the struct instead of passing any parameters as part of a closure for the Fn is sort of a code smell that turned me off immediately about the library. But thanks for the post.</p></pre>pusic007: <pre><p>How would you improve API for passing Arg to Fn? Would be interesting to hear other opinions, because I was thinking about good way of passing arguments to Fn.</p></pre>mvitorino: <pre><blockquote> <p><a href="https://github.com/ivpusic/grpool" rel="nofollow">https://github.com/ivpusic/grpool</a></p> </blockquote> <p>I would capture the required arguments with a closure, meaning you would only have one argument (the Fn) and could turn your struct into a simple func(). It&#39;s simpler and more generic.</p> <p>Something like this:</p> <pre><code>pool := grpool.NewPool(10, 50) defer pool.Release() arg := &#34;world&#34; for i := 0; i &lt; 10; i++ { pool.JobQueue &lt;- func() { fmt.Printf(&#34;hello %s\n&#34;, arg) } } </code></pre></pre>mvitorino: <pre><p>Also instead of returning *Pool on NewPool consider returning an interface.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

656 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传