英文:
golang string channel send/receive inconsistency
问题
新手上路。我正在使用1.5.1版本。我试图根据传入的通道累积一个单词列表。然而,在测试过程中,我的输入通道(wdCh)有时会得到空字符串("")。我感到困惑。我不想在将其累积计数添加到我的映射中之前对空字符串进行测试。对我来说感觉像是一个hack。
package accumulator
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
var words map[string]int
func Accumulate(wdCh chan string, closeCh chan bool) {
words = make(map[string]int)
for {
select {
case word := <-wdCh:
fmt.Printf("word = %s\n", word)
words[word]++
case <-closeCh:
return
}
}
}
func pushWords(w []string, wdCh chan string) {
for _, value := range w {
fmt.Printf("sending word = %s\n", value)
wdCh <- value
}
close(wdCh)
}
func TestAccumulate(t *testing.T) {
sendWords := []string{"one", "two", "three", "two"}
wMap := make(map[string]int)
wMap["one"] = 1
wMap["two"] = 2
wMap["three"] = 1
wdCh := make(chan string)
closeCh := make(chan bool)
go Accumulate(wdCh, closeCh)
pushWords(sendWords, wdCh)
closeCh <- true
close(closeCh)
assert.Equal(t, wMap, words)
}
以上是要翻译的内容。
英文:
New to go. I'm using 1.5.1. I'm trying to accumulate a word list based on an incoming channel. However, my input channel (wdCh) is sometimes getting the empty string ("") during testing. I'm perplexed. I'd rather not have a test for the empty string before I add its accumulated count in my map. Feels like a hack to me.
package accumulator
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
var words map[string]int
func Accumulate(wdCh chan string, closeCh chan bool) {
words = make(map[string]int)
for {
select {
case word := <-wdCh:
fmt.Printf("word = %s\n", word)
words[word]++
case <-closeCh:
return
}
}
}
func pushWords(w []string, wdCh chan string) {
for _, value := range w {
fmt.Printf("sending word = %s\n", value)
wdCh <- value
}
close(wdCh)
}
func TestAccumulate(t *testing.T) {
sendWords := []string{"one", "two", "three", "two"}
wMap := make(map[string]int)
wMap["one"] = 1
wMap["two"] = 2
wMap["three"] = 1
wdCh := make(chan string)
closeCh := make(chan bool)
go Accumulate(wdCh, closeCh)
pushWords(sendWords, wdCh)
closeCh <- true
close(closeCh)
assert.Equal(t, wMap, words)
}
答案1
得分: 2
请查看这篇关于channel-axioms的文章。看起来在关闭wdCh
通道和在closeCh
通道上发送true之间存在竞争。
因此,结果取决于pushWords
返回和Accumulate
之间哪个先被调度。
如果TestAccumulate
先运行,向closeCh
发送true,那么当Accumulate
运行时,它会选择这两个通道中的任意一个,因为它们都可以运行,因为pushWords
关闭了wdCh
。
> 从关闭的通道接收立即返回零值。
在closedCh
被标记之前,Accumulate
将随机将一个或多个空字符串放入映射中。
如果Accumulate
先运行,那么它很可能会在循环中放入许多空字符串,直到TestAccumulate
运行并最终在closeCh
上发送信号。
一个简单的修复方法是将
close(wdCh)
移动到在closeCh
上发送true
之后。这样,直到在closeCh
上发出信号之后,wdCh
才会返回零值。此外,closeCh <- true
会阻塞,因为closeCh
没有缓冲区大小,所以只有在确保Accumulate
已经完成无限循环之后,wdCh
才会被关闭。
英文:
Check out this article about channel-axioms. Looks like there's a race between closing wdCh
and sending true on the closeCh
channel.
So the outcome depends on what gets scheduled first between pushWords
returning and Accumulate
.
If TestAccumulate
runs first, sending true on closeCh
, then when Accumulate
runs it picks either of the two channels since they can both be run because pushWords
closed wdCh
.
> A receive from a closed channel returns the zero value immediately.
Until closedCh
is signaled, Accumulate
will randomly put one or more empty "" words in the map.
If Accumulate
runs first then it's likely to put many empty strings in the word map as it loops until TestAccumulate
runs and finally it sends a signal on closeCh
.
An easy fix would be to move
close(wdCh)
after sending true
on the closeCh
. That way wdCh
can't return the zero value until after you've signaled on the closeCh
. Additionally, closeCh <- true
blocks because closeCh
doesn't have a buffer size, so wdCh
won't get closed until after you've guaranteed that Accumulate
has finished looping forever.
答案2
得分: -2
我认为原因是当你关闭通道时,"select"语句仍然会接收到信号。
所以当你在"func pushWords"中关闭"wdCh"时,Accumulate中的循环会从"<-wdCh"接收到信号。
也许你应该添加一些代码来测试通道关闭后的操作!
for {
select {
case word, ok := <-wdCh:
if !ok {
fmt.Println("通道wdCh已关闭!")
continue
}
fmt.Printf("word = %s\n", word)
words[word]++
case <-closeCh:
return
}
}
英文:
I think the reason is when you close the channle, "select" will although receive the signal.
So when you close "wdCh" in "func pushWords", the loop in Accumulate will receive signal from "<-wdCh".
May be you should add some code to test the action after channel is closed!
for {
select {
case word, ok := <-wdCh:
if !ok {
fmt.Println("channel wdCh is closed!")
continue
}
fmt.Printf("word = %s\n", word)
words[word]++
case <-closeCh:
return
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论