[Question] Is it ok to store a context.CancelFunc inside a struct?

agolangf · · 69 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>type Server struct { actionChan chan func() cancel context.CancelFunc }</p> <pre><code>NewServer() *Server { ctx, cancel := context.WithCancel(context.Background()) s := &amp;Server{ cancel: cancel, actionChan: make(chan func(), 1), } go s.actorLoop(ctx) return s } func (s *Server) actorLoop(ctx context.Context) { for { select { case &lt;-ctx.Done(): return case f &lt;-actionChan: // do some stuf.... f() } } } func (s *Server) Stop() { s.cancel() } </code></pre> <hr/>**评论:**<br/><br/>BurpsWangy: <pre><p><a href="https://golang.org/pkg/context/" rel="nofollow">https://golang.org/pkg/context/</a></p> <p>5th paragraph down in the &#39;overview&#39;...</p></pre>slowratatoskr: <pre><p>i know that context shouldnt be stored, i also did my research and learned from the mailing list that there are exceptions to this &#34;rule&#34;. note that what im storing is the cancelFunc, not the context itself. if you can enlighten me why a cancelfunc inside a struct is a bad idea then go on.</p></pre>BurpsWangy: <pre><p>I think you&#39;ll be fine. Just referencing what the package documentation says. I think it only even refers to it being that way because it&#39;s more explicit. If you make it part of the function definition, you require every call to pass a context.</p></pre>_zombiezen_: <pre><p>Storing a CancelFunc in this way (for stopping a background goroutine) is exactly what you should do. The one thing I would add to that code snippet is that <code>Stop()</code> ought to block on actorLoop returning. Usually I do this by having the background goroutine defer a close of a chan struct{}, and then have the stop function wait on it.</p> <p>The reason for this is to ensure that by the time Stop returns, the goroutine is no longer doing work. This can avoid races in some cases, but in all cases avoids leaking goroutines in case your background goroutines is doing runaway work.</p></pre>fortytw2: <pre><p>There&#39;s absolutely no need to use a context in this scenario. Here&#39;s an example of a determinsitic shutdown via two channels <a href="https://gist.github.com/fortytw2/f716aff1d11fb90d4696bb7bf582464e" rel="nofollow">https://gist.github.com/fortytw2/f716aff1d11fb90d4696bb7bf582464e</a></p> <p>Alternatively, you can use a channel of channels to do the same thing, I find two to be a bit easier to grok though.</p></pre>slowratatoskr: <pre><blockquote> <p><a href="https://gist.github.com/fortytw2/f716aff1d11fb90d4696bb7bf582464e" rel="nofollow">https://gist.github.com/fortytw2/f716aff1d11fb90d4696bb7bf582464e</a></p> </blockquote> <p>thanks!</p></pre>AgentSS: <pre><p>That seems fine to me.</p> <p>I&#39;d add a Waitgroup, or a deferred channel close to actorLoop, such that Stop() can wait for the actorLoop to exit.</p> <p>In particular, I&#39;d have Stop() take a context, and block until either actorLoop exits, or the context cancels, whichever comes first. (It should also be safe to be called multiple times). This is basically the pattern that net/http&#39;s Shutdown method takes: <a href="https://godoc.org/net/http#Server.Shutdown" rel="nofollow">https://godoc.org/net/http#Server.Shutdown</a>.</p> <p>One other thing I would recommend is to have actionChan be a &#34;chan func(context.Context)&#34; and propagate actorLoop&#39;s ctx into f. That way, when someone calls Stop(), the action functions will also be cancelled promptly.</p></pre>
69 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传