2013-12-08 2 views
2

Я новичок в Go и нахожусь в тупике по поводу того, что кажется довольно редким состоянием гонки с очень маленьким блоком кода, работающим на Linux с версией Go 1.2.Состояние гонки с простым каналом в Go?

В принципе, я создаю канал для int, запускаю рутину для чтения с канала, а затем записываю на int один канал.

package main 

import "fmt" 

func main() { 
    channel := make(chan int) 

    go func() { 
     number := <- channel 
     fmt.Printf("GOT IT: %d\n", number) 
    }() 

    fmt.Println("[+] putting num on channel") 
    channel <- 42 
    fmt.Println("[-] putting num on channel") 
} 

Выход около 90% от времени, как и ожидалось:

$ go run test.go 
[+] putting num on channel 
GOT IT: 42 
[-] putting num on channel 

Однако около 10% времени, процедура идти просто не читает номер из канала и ничего не выводит :

$ go run test.go 
[+] putting num on channel 
[-] putting num on channel 

Я озадачен, потому что этот код очень похож на пример, в https://gobyexample.com/channels, (который я не имею эту проблему с), за исключением того, что я читаю из канала в моем ходу рутина вместо того, чтобы писать на канал.

Есть ли у меня фундаментальное непонимание того, как работают каналы или что-то еще здесь играет?

ответ

6

Вы должны ждать, пока ваш goroutine не выполняет, а затем ваш, к примеру, вы можете сделать это с sync.WaitGroup:

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    var wg sync.WaitGroup 

    channel := make(chan int) 
    wg.Add(1) 

    go func() { 
    number := <-channel 
    fmt.Printf("GOT IT: %d\n", number) 
    wg.Done() 
    }() 

    fmt.Println("[+] putting num on channel") 
    channel <- 42 
    wg.Wait() 
    fmt.Println("[-] putting num on channel") 
} 

(Goplay: http://play.golang.org/p/VycxTw_4vu)

Также вы можете сделать это с " уведомление канала», что указывает на то, что работа делается:

package main 

import "fmt" 

func main() { 
    channel := make(chan int) 
    done := make(chan bool) 

    go func() { 
    number := <-channel 
    fmt.Printf("GOT IT: %d\n", number) 
    done <- true 
    }() 

    fmt.Println("[+] putting num on channel") 
    channel <- 42 
    <-done 
    fmt.Println("[-] putting num on channel") 
} 

(GoPlay: http://play.golang.org/p/fApWQgtr4D)

3

Вы, кажется, ожидаете, что получающий горутин будет запущен до завершения второго fmt.Println. Это не гарантируется. Если программа завершается, goroutines не гарантированно достигает конца своих функций.

Когда вы видите вывод, который не отображает сообщение «GOT IT», канал доставил свое сообщение, но функция main была выполнена до того, как горутин сделал. Программа завершается, и goroutine никогда не получает возможность позвонить fmt.Printf

В примере вы цитируемой функция main заканчивается так:

не
go func() { messages <- "ping" }() 
msg := <-messages 
fmt.Println(msg) 

Поскольку функциональные блоки main до тех пор, получает сообщение , горутин всегда заканчивается в этом примере. В вашем коде ваш goroutine выполняет шаг после его получения из канала, и не определено, будет ли goroutine или основная функция выполнять следующую строку после приема.

2

У вас есть два goroutines, один в main() (который неявно является goroutine) и анонимным.

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

На данный момент, код остается в главном() goroutine выглядит следующим образом:

fmt.Println("[-] putting num on a channel") 

и слева в анонимной goroutine код выглядит следующим образом:

fmt.Println("GOT IT: %d\n", number) 

Теперь вы» re racing: выход из этих Println s может отображаться в любом порядке или даже смешиваться. Когда Println() от основной отделки, следующая вещь, которая случится на этом goroutine, будет заключаться в том, что ваша программа будет остановлена. Это может помешать появлению некоторых или всех из Println из анонимного горутина.

+0

Лучший ответ, объясняющий, ПОЧЕМУ, что он НЕ работает, а не предлагает рабочее решение ... – noooooooob

Смежные вопросы