Голанг низ канал изпращане/получаване несъответствие

Ново в движение. Аз съм с 1.5.1. Опитвам се да натрупам списък с думи въз основа на входящ канал. Моят входен канал (wdCh) обаче понякога получава празен низ ("") по време на тестване. Аз съм в недоумение. Предпочитам да нямам тест за празния низ, преди да добавя натрупания му брой в моята карта. Чувствам се като хак за мен.

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)
}

person Jacob    schedule 04.10.2015    source източник


Отговори (2)


Вижте тази статия за channel-axioms. Изглежда, че има надпревара между затварянето на wdCh и изпращането на true в канала closeCh.

Така че резултатът зависи от това какво е насрочено първо между pushWords завръщане и Accumulate.

Ако TestAccumulate се изпълнява първи, изпращайки true на closeCh, тогава, когато Accumulate се изпълнява, той избира един от двата канала, тъй като и двата могат да бъдат стартирани, защото pushWords затвори wdCh.

Получаване от затворен канал незабавно връща нулевата стойност.

Докато closedCh не бъде сигнализирано, Accumulate произволно ще постави една или повече празни "" думи в картата.

Ако Accumulate се изпълни първо, тогава има вероятност да постави много празни низове в картата на думите, докато се зацикли, докато TestAccumulate се изпълни и накрая изпрати сигнал на closeCh.

Лесно решение би било да се преместите

close(wdCh)

след изпращане на true на closeCh. По този начин wdCh не може да върне нулевата стойност, докато не сигнализирате на closeCh. Освен това, closeCh <- true блокира, защото closeCh няма размер на буфера, така че wdCh няма да се затвори, докато не гарантирате, че Accumulate е завършил цикъла завинаги.

person lucidquiet    schedule 04.10.2015
comment
Или просто премахнете напълно този оператор за затваряне, тъй като той не прави нищо след излизането на този цикъл. - person JimB; 04.10.2015
comment
Преместването на затварянето след като направих close(closeCh) сработи. - person Jacob; 05.10.2015

Мисля, че причината е, че когато затворите канала, "select" ще получи сигнала.

Така че, когато затворите "wdCh" във "func pushWords", цикълът в Accumulate ще получи сигнал от "‹-wdCh". Може би трябва да добавите някакъв код, за да тествате действието след затваряне на канала!

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
    }
}
person linuxr    schedule 04.10.2015
comment
Това кара затворения канал да превърне цикъла в натоварено изчакване, което от своя страна може да причини повече проблеми - person JimB; 08.11.2015