Channels as a work queue

blov · · 509 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>So, I&#39;ve been writing a game in go, and I was wondering how I could handle data coming from the network.</p> <p>I was thinking about just putting all the data coming from the wire inside a channel, and consuming that channel every iteration of the game loop, but once I try to consume that channel, it blocks.</p> <p>``` message = bytes.Trim(message, &#34; \n\t&#34;) var input Input if err := GetInput(message, &amp;input); err != nil { fmt.Println(err) }</p> <pre><code>//fmt.Println(input) c.hub.Inputs = append(c.hub.Inputs, input) </code></pre> <p>```</p> <p>Then: ``` func (system InputSystem) Loop() { for _, input := range system.Hub.Inputs { fmt.Println(input) }</p> <p>system.Hub.Inputs = make([]Input, 0) } ```</p> <p>How can I read from the channel until it&#39;s empty, then have go move on that loop?</p> <hr/>**评论:**<br/><br/>demitriousk: <pre><p>sounds like you want a for{ select { } } with one of the cases being a default or being a ticker from the time package</p></pre>SmokyQuarks: <pre><p>Let me recommend this blogpost as a good introduction on what each type of channel means. It really clarified a bunch of stuff for me: <a href="https://www.goinggo.net/2017/10/the-behavior-of-channels.html" rel="nofollow">https://www.goinggo.net/2017/10/the-behavior-of-channels.html</a></p></pre>donja_crtica: <pre><p>Tnx</p></pre>TrueFurby: <pre><p><a href="https://golangbot.com/channels/" rel="nofollow">https://golangbot.com/channels/</a></p></pre>donte9181: <pre><p>Here&#39;s a really bare bones attempt to show how you might do this.</p> <p><a href="https://play.golang.org/p/0bqMNnDOK1" rel="nofollow">https://play.golang.org/p/0bqMNnDOK1</a></p> <p>The crux of this is that you can actually call <code>len(mychannel)</code> to query the length of your work queue before attempting to read from it. So don&#39;t use the <code>&lt;-</code> operator if the length of the queue is zero. A few other things to think about which I note in my example:</p> <ul> <li>It caps the max number of jobs at 5, so any other jobs added before the game loop can process some will be lost. Generally speaking you&#39;ll put this number much higher and it shouldn&#39;t be an issue unless something is really really wrong, but you don&#39;t want it to grow to infinity. The &#34;producer&#34; code won&#39;t add any more work if the channel is full, so it won&#39;t block either.</li> <li>I added a boolean function &#34;HasTimeToProcessJob&#34; which is in the loop that tries to process jobs from the queue. It&#39;s probably a good idea to limit the amount of time each frame can spend processing jobs so that you don&#39;t skip frames doing non-drawing work. For instance maybe you only want to dedicate 5ms per frame on jobs even if there&#39;s more work - that will get done next frame. My implementation returns &#39;true&#39; so we&#39;ll always process the entire queue, but you might want some better logic.</li> </ul> <p>Otherwise, this isn&#39;t meant to be a serious game engine implementation, but hopefully you get a feel for how you can integrate a work queue into your standard game loop flow.</p></pre>carsncode: <pre><p>I can&#39;t say I&#39;ve ever seen this pattern used in Go. Usually you see a <code>select</code> to do non-blocking channel ops; the implementation is simpler and clearer, doesn&#39;t risk races when you have multiple consumers, and uses the existing logic for checking a channel that&#39;s built into the runtime rather than recreating it.</p></pre>donte9181: <pre><p>That&#39;s a good point. In this case I don&#39;t worry about the multiple consumers because this is being used in a game loop where I presume they&#39;re running in a single-threaded manner. But you&#39;re right that the select does provide some additional clarity and actually makes short circuiting the loop when you run out of time this frame much easier to implement.</p> <p>Here&#39;s an updated version that&#39;s hopefully a bit more idiomatic in that respect:</p> <p><a href="https://play.golang.org/p/U618DON5Hv" rel="nofollow">https://play.golang.org/p/U618DON5Hv</a></p> <p>Thanks for the feedback - and for being civil about it :)</p></pre>fllr: <pre><p>How would you do this instead?</p></pre>earthboundkid: <pre><p>The call to len is totally race-y. It’s not a great idea. </p></pre>elagergren: <pre><p>Racey, yes, but with the caveat that you won&#39;t get a partial read. adg (or somebody else) confirmed it on the mailing list a while back. You might get a stale read or double up, but that&#39;s it.</p></pre>fllr: <pre><p>Why would this not be ready for a serious engine? What would you do this instead?</p></pre>donte9181: <pre><p>It was more a comment about the fact that my game loop was just a counter from 1-100 and I hard-coded DeltaTime to about 60 frames per second rather than actually figuring out how much time elapsed since the previous frame.</p> <p>For most games this basic setup is more than sufficient, so keep doing what you&#39;re doing. For advanced cases, it&#39;s not uncommon to have multiple different types of timers. You have Update() that is called once per frame, but some other callback that occurs with a more fixed timer (independent of frame rate) for things like physics calculations that are less tolerant of large gaps in time. Here&#39;s Unity&#39;s documentation for some of the ways they slice and dice the game loop: <a href="https://docs.unity3d.com/Manual/ExecutionOrder.html" rel="nofollow">https://docs.unity3d.com/Manual/ExecutionOrder.html</a></p></pre>carsncode: <pre><p>Unity is a good example, especially because it&#39;s so well documented on their site, and it&#39;s so widely used you can find discussion and explanation of how it works everywhere online.</p></pre>

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

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