Как работают каналы Go
Недавно начал изучать Go. На данный момент изучаю каналы. У меня вопрос, почему в этой программе значение в канал передается:
package main
import (
"fmt"
)
func f (c chan string){
fmt.Println(<-c)
}
func main() {
//r := bufio.NewReader(os.Stdin)
c := make(chan string)
go f(c)
//text, _ := r.ReadString('\n')
//fmt.Println(text)
fmt.Println("Hello")
c <- "jhj"
}
А в этой нет
package main
import (
"bufio"
"fmt"
"os"
)
func f (c chan string){
fmt.Println(<-c)
}
func main() {
r := bufio.NewReader(os.Stdin)
c := make(chan string)
go f(c)
text, _ := r.ReadString('\n')
fmt.Println(text)
//fmt.Println("Hello")
c <- "text"
}
Ответы (2 шт):
Ни ваш первый, ни второй вариант не даёт гарантий работы. Всё потому
что ваш main не ждёт выполнения работы. Добавьте
синхронизацию между f и main. Самое
примитивное — добавить в конец функции
main код select{}, который заблокирует
выполнение main.
На самом деле значение в канал передается в обоих случаях. То, что вы наблюдаете, является классической гонкой (race condition).
Вы запускаете функцию f в отдельной горутине, которая блокируется на чтение из канала c. Как только вы записываете в канал какую-либо строку (jhj в первом случае и text во втором), функция разблокируется и продолжает выполнение. Проблема в том, что main тоже продолжает выполнение после записи в канал параллельно с функцией f на другом ядре процессора и успевает завершится до того, как выполнится fmt.Println в функции f. Но это никак не гарантируется и поэтому и называется гонкой.
Более того, даже исполнение на одном процессорном ядре не гарантирует, что функция fmt.Println внутри f выполнится раньше, чем завершится main. Как правильно заметил @Ainar-G, вам нужно добавить синхронизацию и дождаться завершения f. Вот один из вариантов как это можно сделать:
package main
import (
"bufio"
"fmt"
"os"
"sync"
)
var wg sync.WaitGroup
func f(c chan string) {
fmt.Println(<-c)
// сигнализируем о завершении функции
// по сути делаем wg.Add(-1)
wg.Done()
}
func main() {
r := bufio.NewReader(os.Stdin)
c := make(chan string)
wg.Add(1) // говорим что будем дожидаться завершения одной функции
go f(c)
text, _ := r.ReadString('\n')
fmt.Println(text)
c <- "text"
// ждем завершения функции f
// Wait вернется, когда счетчик внутри wg станет равным 0
wg.Wait()
}