Golang字符串通道的发送/接收不一致性

huangapple go评论91阅读模式
英文:

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 (
    &quot;fmt&quot;
    &quot;github.com/stretchr/testify/assert&quot;
    &quot;testing&quot;
)

var words map[string]int

func Accumulate(wdCh chan string, closeCh chan bool) {
    words = make(map[string]int)
    for {
        select {
        case word := &lt;-wdCh:
            fmt.Printf(&quot;word = %s\n&quot;, word)
            words[word]++
        case &lt;-closeCh:
            return
        }
    }
}

func pushWords(w []string, wdCh chan string) {
    for _, value := range w {
        fmt.Printf(&quot;sending word = %s\n&quot;, value)
        wdCh &lt;- value
    }
    close(wdCh)
}

func TestAccumulate(t *testing.T) {
    sendWords := []string{&quot;one&quot;, &quot;two&quot;, &quot;three&quot;, &quot;two&quot;}
    wMap := make(map[string]int)
    wMap[&quot;one&quot;] = 1
    wMap[&quot;two&quot;] = 2
    wMap[&quot;three&quot;] = 1

    wdCh := make(chan string)
    closeCh := make(chan bool)

    go Accumulate(wdCh, closeCh)
    pushWords(sendWords, wdCh)

    closeCh &lt;- 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 &lt;- 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 := &lt;-wdCh:
		if !ok {
			fmt.Println(&quot;channel wdCh is closed!&quot;)
			continue
		}
		fmt.Printf(&quot;word = %s\n&quot;, word)
		words[word]++
	case &lt;-closeCh:
		return
	}
}

huangapple
  • 本文由 发表于 2015年10月4日 08:26:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/32928843.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定