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)
}
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.
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
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.