Why is this code snippet blocking?

blov · · 501 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Sorry if a noob question. I just started learning Go yesterday, went through go tour, a bunch of blog posts, some example repos; and was feeling comfortable giving it a shot. </p> <p>I am trying to read a file of email addresses, and return a channel from a function of <code>mail.Address</code> type. I can&#39;t understand why it blocks without any output or <code>deadlock</code> failure. Here is the code</p> <pre><code>package main import ( &#34;bufio&#34; &#34;fmt&#34; &#34;net/mail&#34; &#34;os&#34; ) const addressListFile = &#34;./emails.txt&#34; func GetAddressChan(path string) &lt;-chan *mail.Address { addrFile, err := os.Open(path) defer addrFile.Close() if err != nil { panic(err) } scanner := bufio.NewScanner(addrFile) if err := scanner.Err(); err != nil { panic(err) } addrCh := make(chan *mail.Address) for scanner.Scan() { addr, err := mail.ParseAddress(scanner.Text()) if err != nil { panic(err) } addrCh &lt;- addr } close(addrCh) return addrCh } func main() { addrCh := GetAddressChan(addressListFile) go func() { for addr := range addrCh { fmt.Println(addr) } }() } </code></pre> <p>It works if instead of return a channel from <code>GetAddressChan</code>, I pass it one as an argument and run <code>GetAddressChan</code> as a goroutine.</p> <p>Any comments on how I am not writing idiomatic Go are welcome as well.</p> <p>Edit: I&#39;ve also tried putting the goroutine inside <code>GetAddressChan</code> instead of <code>main</code> as well, in which case the program exits without printing anything:</p> <pre><code>addrCh := make(chan *mail.Address) go func() { for scanner.Scan() { addr, err := mail.ParseAddress(scanner.Text()) if err != nil { panic(err) } addrCh &lt;- addr } close(addrCh) }() return addrCh </code></pre> <p>Update: It is <code>bufio.Scan</code> that is causing this issue I think. It works as expected if I use a normal slice/array of strings instead of reading using <code>bufio.Scanner</code></p> <hr/>**评论:**<br/><br/>Sythe2o0: <pre><p><code>addrCh &lt;- addr</code> blocks until something takes the thing its trying to send, which can&#39;t happen if the goroutine at the end of main hasn&#39;t spawned yet. You could reorganize it so that <code>GetAddressChan</code> had its second half after a <code>go</code> keyword, or you could do what you suggested and pass in the channel to use.</p> <p>As far as idiomatic go goes, you probably already know that <code>panic</code> is generally to be avoided. It&#39;d be more idiomatic if <code>GetAddressChan</code> returned a channel and an error.</p></pre>vehlad_durjan: <pre><p>Thank your for your note.</p> <p>I don&#39;t want to have to pass the function a channel. I want it to return a channel so I can treat it as a generator. I have tried restructuring the code so the generator send part is in a goroutine, but even then it doesn&#39;t work.</p> <p>Please check the update to the post.</p></pre>Sythe2o0: <pre><p>Any goroutines left running when the end of <code>main</code> is reached will stop execution. Adding in some blocking operation at the end of <code>main</code> (or in this case, not putting the end of main after <code>go</code>) will prevent this.</p></pre>vehlad_durjan: <pre><p>It doesn&#39;t seem to make a difference if I block the execution after <code>go</code> statement in <code>main</code>:</p> <pre><code>ch := make(chan int) _ = &lt;-ch </code></pre> <p>Only thing that seem to work is if I don&#39;t use any goroutine in either <code>main</code> or <code>GetAddressChan</code>, but make <code>addrCh</code> a buffered channel with buffer &gt; number of email addresses in the file. (or if I block main from exiting <strong>and</strong> use said buffered channel)</p></pre>Sythe2o0: <pre><p><del>Because your range over addrCh will end as soon as there&#39;s nothing left there, which will be immediately, because the goroutine sending things to the channel hasn&#39;t spawned yet.</del> &lt;- this is wrong, sorry I rarely range over channels myself</p> <p>Then I&#39;m not sure, can you make a link to a <a href="https://play.golang.org/" rel="nofollow">https://play.golang.org/</a> snippet with the updated code?</p> <p>See this: <a href="https://play.golang.org/p/epTpwuycD0" rel="nofollow">https://play.golang.org/p/epTpwuycD0</a></p></pre>vehlad_durjan: <pre><p>Won&#39;t the goroutine for <code>addrCh</code> spaws as soon as <code>GetAddressChan</code> gets called and blocks until someone receives a value? At least in the second case in which I wrap <code>for</code> loop sending the values to <code>addrCh</code> in <code>GetAddressChan</code> in a goroutine instead of wrapping the <code>for</code> on <code>range</code> which receives values. It doesn&#39;t work in that case either. </p> <p>All this seemed easier when I was reading the code/doing examples only. It seem very much like a simple example code, but I am unable to figure out what I am doing wrong.</p></pre>Sythe2o0: <pre><p>I&#39;m not following what you&#39;re saying. Can you write up what you&#39;re trying in a snippet? This is an example of how this would work, without using the mail stuff: <a href="https://play.golang.org/p/7yvWLDMy4M" rel="nofollow">https://play.golang.org/p/7yvWLDMy4M</a></p></pre>vehlad_durjan: <pre><p>It&#39;s <code>bufio.Scan</code>&#39;s doing I think. If I use a normal slice of strings instead of <code>bufio.Scan</code>, everything works as expected. I should study it more to find out what it is doing that is causing this, and how to work around it.</p></pre>bhiestand: <pre><p>Use a go routine in the function, and not in main. You don&#39;t need a buffered channel. You want the program to run until all addresses have been printed, but main will exit as soon as it starts the printing goroutine, as written.</p> <p><a href="https://play.golang.org/p/Fu6qVdDesZ" rel="nofollow">https://play.golang.org/p/Fu6qVdDesZ</a></p></pre>

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

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