Переписать канал во время прослушивания и обработки петли - PullRequest
0 голосов
/ 02 июля 2018

Пробуя несколько экспериментов на каналах, я придумал следующий код:

var strChannel = make(chan string, 30)
var mutex = &sync.Mutex{}

func main() {

    go sampleRoutine()

    for i := 0; i < 10; i++ {
        mutex.Lock()
        strChannel <- strconv.FormatInt(int64(i), 10)
        mutex.Unlock()
        time.Sleep(1 * time.Second)
    }

    time.Sleep(10 * time.Second)
}

func sampleRoutine() {
/*  A: for msg := range strChannel{*/
/*  B: for {
        msg := <-strChannel*/
        log.Println("got message ", msg, strChannel)
        if msg == "3" {
            mutex.Lock()
            strChannel = make(chan string, 20)
            mutex.Unlock()
        }
    }
}

В основном здесь, при прослушивании данного канала, я назначаю переменную канала новому каналу в определенном состоянии (здесь, когда msg == 3).

Когда я использую код в блоке комментариев B, он работает как положено, то есть цикл переходит на вновь созданный канал и печатает 4-10.

Однако блок комментария A, который, как я считаю, является просто другим способом написания цикла, не работает, т. Е. После печати «3» он останавливается.

Может кто-нибудь сообщить мне причину такого поведения?

И есть ли такой код, когда обычное прослушивание канала создает новый сейф?

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

В Go оператор for оценивает значение справа от range до начала цикла.

Это означает, что изменение значения переменной справа от range не будет иметь никакого эффекта. Таким образом, в вашем коде, в варианте A, msg постоянно повторяется по оригинальному каналу и никогда не изменяется. В Varaint B он работает так, как задумано, так как канал оценивается за одну итерацию.

Концепция немного хитрая. Это не означает, что вы не можете изменить элементов из slice или map с правой стороны range. Если вы посмотрите в него глубже, вы обнаружите, что в Go map и slice хранит указатель, а изменение его элемента не приводит к изменению этого указателя, поэтому он имеет эффекты.

Это еще сложнее в случае array. Изменение предмета array справа range не имеет никакого эффекта. Это связано с механизмом Go о сохранении массива как значения.

Примеры игровых площадок: https://play.golang.org/p/wzPfGHFYrnv

0 голосов
/ 02 июля 2018
  1. Для отправки и чтения из канала не необходимо защищать мьютексом: они сами действуют как примитивы синхронизации (это одна из основных идей отправки / получение) на канале.

  2. Нет разницы между вариантом A и B, потому что вы не закрываете канал. Но ...

  3. Изменение нового канала на strChannel во время итерации старого канала неверно. Не делай этого. Даже с вариантом B. Думайте о range strChannel как о «пожалуйста, диапазон значений в канале, которые в данный момент хранятся в переменной strChannel». Этот диапазон будет «продолжать на исходном канале», и сохранение нового канала в той же переменной не изменит этого. Избегайте такого кода!

...