<p>So, I'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, " \n\t")
var input Input
if err := GetInput(message, &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'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'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't use the <code><-</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'll put this number much higher and it shouldn't be an issue unless something is really really wrong, but you don't want it to grow to infinity. The "producer" code won't add any more work if the channel is full, so it won't block either.</li>
<li>I added a boolean function "HasTimeToProcessJob" which is in the loop that tries to process jobs from the queue. It's probably a good idea to limit the amount of time each frame can spend processing jobs so that you don't skip frames doing non-drawing work. For instance maybe you only want to dedicate 5ms per frame on jobs even if there's more work - that will get done next frame. My implementation returns 'true' so we'll always process the entire queue, but you might want some better logic.</li>
</ul>
<p>Otherwise, this isn'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't say I'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't risk races when you have multiple consumers, and uses the existing logic for checking a channel that's built into the runtime rather than recreating it.</p></pre>donte9181: <pre><p>That's a good point. In this case I don't worry about the multiple consumers because this is being used in a game loop where I presume they're running in a single-threaded manner. But you'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's an updated version that'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'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'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're doing. For advanced cases, it'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's Unity'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's so well documented on their site, and it's so widely used you can find discussion and explanation of how it works everywhere online.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传