Почему некоторые операции с каналом блокируются, а некоторые нет - PullRequest
0 голосов
/ 08 мая 2020

Я заметил несоответствие в том, как некоторые статьи (и собственная документация golang) описывают операции канала и то, что я видел на самом деле.

Это связано с тем, как Go блоков на чтение / запись канала. Я теперь читал во многих местах, что горутина блокирует выполнение всякий раз, когда она видит чтение или запись в канал, что означает, что она либо ожидает получения данных, либо ждет, пока другая горутина получит данные из канала.

Но если вы посмотрите на следующий пример, это явно не то, что происходит при второй записи.

package main

import (
    "fmt"
)

func firstFunc(ch chan string) {
    fmt.Println("firstFunc Hello", <-ch)
    fmt.Println("firstFunc() carries on getting called")
}

func secondFunc(ch chan string) {
    fmt.Println("secondFunc Hello", <-ch)
    fmt.Println("secondFunc() carries on getting called")
}

func main() {
    fmt.Println("main() started")
    c1 := make(chan string)
    c2 := make(chan string)

    go firstFunc(c1)
    go secondFunc(c2)

    c1 <- "John"
    c2 <- "Bob"

    fmt.Println("main() ended")
}

Вот как я интерпретирую, что Go выполняет этот код:

  1. он печатает запущенное main () сообщение, создает каналы c1 и c2 и ставит в очередь firstFun c и secondFun c горутины (но не выполняет их в этот момент)
  2. он попадает в c1 < - «Джон» и блокирует его до тех пор, пока другая горутина не прочитает из этого канала
  3. , в этот момент он планирует firstFun c, который читает из c1 и продолжает выполнение остальной части кода до конца функции
  4. main () снова переназначен, и следующая строка - C2 <- "Bob", на этом этапе я бы подумал, что main () должен снова заблокироваться, как это было с "John" и дождитесь, пока secondFun c прочитает его, прежде чем продолжить. Но этого не происходит. Результат: </li>
main() started
firstFunc Hello John
firstFunc() carries on getting called
main() ended

он не просто блокирует запись в «Боб», а вместо этого продолжает выполнение до тех пор, пока main () не будет завершен, и никогда не планирует secondFun c.

Это стало для меня препятствием на пути к изучению Go, поскольку я не уверен, что это статьи, которым я не могу доверять, или есть пробел в моем понимании.

Я был бы очень признателен за помощь в этом.

Ответы [ 3 ]

4 голосов
/ 08 мая 2020

Запись небуферизованного канала будет заблокирована до тех пор, пока другая горутина не прочитает из него. Когда это происходит, включаются как чтение, так и запись горутины. После этого нет никаких гарантий относительно того, как будет происходить выполнение.

В вашем примере после того, как firstFunc считывает из канала, он выполняется до завершения. Основная горутина записывает на второй канал, что позволяет secondFunc, но прежде, чем у него появится возможность завершить sh запись, основная горутина заканчивается.

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

3 голосов
/ 08 мая 2020

Вы не видите вывода из secondFunc, потому что main завершается до того, как у него есть возможность выполнить. Простой способ решить эту проблему - добавить time.Sleep(time.Second) перед выводом «main () finished».

Более правильным способом было бы использовать группы ожидания, которые выглядели бы следующим образом:

wg := sync.WaitGroup{}
wg.Add(2)
go func() { firstFunc(c1); wg.Done() }()
go func() { secondFunc(c2); wg.Done() }()
... // write to channels
wg.Wait() // This blocks till both the go-routines are done
3 голосов
/ 08 мая 2020

Не блокируется по той причине, что есть 2 слушателя канала (firstFun c и secondFun c). Они в произвольном порядке прочитают ваше сообщение (в зависимости от того, что проснется первым). Таким образом, блокировка есть, но она не заметна для активных слушателей. Единственное, что вы увидите, это то, что иногда вы увидите вывод firstFun c first, а иногда и secondFun c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...