Порядок блокировки каналов - PullRequest
0 голосов
/ 30 мая 2019

Я пытаюсь понять, как работают каналы в Голанге.Код, который я имею, очень прост, но вывод удивителен.

Как указано в документации: чтение и запись из / в канал блокирует текущую процедуру, поэтому я подумал, что запись в канал заблокирует канал доосновная подпрограмма дает.

package main

func rtn(messages chan<- string) {
    defer close(messages)

    println("p1")
    messages <- "ping1"

    //for i := 0; i < 10000000; i++ { }

    println("p2")
    messages <- "ping2"
}

func main() {
    messages := make(chan string)
    go rtn(messages)

    for msg := range messages {
        println(msg)
    }
}

Я думал, что она напечатает

p1
ping1
p2
ping2

, но на самом деле она печатает

p1
p2
ping1
ping2

1 Ответ

2 голосов
/ 30 мая 2019

Вы используете небуферизованные каналы, которые работают как точка синхронизации между основной и второй программами.

В этом случае вы знаете только, что, когда вторая процедура находится здесь messages <- "ping1", главная находится в строке for msg := range messages. Таким образом, нет гарантии, что основной цикл достигнет println(msg) немедленно. То есть, в то же время вторая программа могла продвинуться и достичь линий println("p2") и messages <- "ping2".

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

package main

func rtn(messages chan<- string, syncChan chan struct{}) {
    defer close(messages)

    println("p1")
    messages <- "ping1"

    //Wait for main goroutine to print its message
    <-syncChan

    //for i := 0; i < 10000000; i++ { }

    println("p2")
    messages <- "ping2"

    //Wait for main goroutine to print its message
    <-syncChan
}

func main() {
    messages := make(chan string)
    syncChan := make(chan struct{})
    go rtn(messages, syncChan)

    for msg := range messages {
        println(msg)
        //Notify the second goroutine that is free to go
        syncChan <- struct{}{}
    }
}

Который печатает результат, который вы ожидали:

p1
ping1
p2
ping2

Вот еще один пример, который выдает результат, который вы ищете. В этом случае основная программа была принудительно заблокирована time.Sleep(). Это сделает вторую процедуру готовой к отправке до того, как получатель будет готов принять. Следовательно, отправитель фактически заблокирует операцию отправки.

package main

import (
    "time"
)

func rtn(messages chan<- string) {
    defer close(messages)

    println("p1")
    messages <- "ping1"

    //for i := 0; i < 10000000; i++ { }

    println("p2")
    messages <- "ping2"
}

func main() {
    messages := make(chan string)
    go rtn(messages)

    //Put main goroutine to sleep. This will make the
    //sender goroutine ready before the receiver. 
    //Therefore it will have to actually block!
    time.Sleep(time.Millisecond * 500)

    for msg := range messages {
        println(msg)
    }
}
...