Всегда тупик с каналами
Я учусь работать с каналами Go и всегда получаю тупики. Что может быть не так с этим кодом? Принтер случайным образом перестает работать, когда размеры массива неравны; Полагаю, это помогло бы как-то уведомить принтер, что приемник перестал работать. Есть идеи как это исправить? Мой код вставлен ниже.
package main
import (
"fmt"
"sync"
)
var wg = sync.WaitGroup{}
var wgs = sync.WaitGroup{}
var sg = make(chan int, 50)
var gp1 = make(chan int, 50)
var gp2 = make(chan int, 50)
func main(){
wgs.Add(2)
go Sender(0)
go Sender(11)
wg.Add(3)
go Receiver()
go Printer()
go Printer2()
wg.Wait()
}
func Sender(start int){
defer wgs.Done()
for i := start; i < 20; i++ {
sg <- i
}
}
func Receiver(){
defer wg.Done()
for i := 0; i < 20; i++{
nr := <- sg
if nr % 2 == 0{
gp1 <- nr
} else{
gp2 <- nr
}
}
}
func Printer(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp1
}
wgs.Wait()
fmt.Println(m)
}
func Printer2(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp2
}
wgs.Wait()
fmt.Println(m)
}
// Better to use this one
// func Receiver(senderChannel <-chan int, printerChannel1 chan<- int, printerChannel2 chan<- int, wg *sync.WaitGroup) {
2 ответа
Отправитель генерирует (я думаю, 28 сообщений) . Примерно половина первых 20 из них идет к одному из gp1 и gp2. Принтер и Принтер2 затем выгрузить сообщения
Проблема в том, что способ, которым Receiver разбивает сообщения, зависит от того, является ли полученное число нечетным или четным. Но вы не контролируете это. Если в очереди одного из принтеров менее 10 элементов, он зависнет
Это одна потенциальная проблема
Ваша основная проблема заключается в том, что все в этом - "мертвый расчет": они ожидают увидеть фиксированное количество сообщений, но это не обязательно совпадает с реальностью. Вы должны настроить каналы так, чтобы они закрывались после того, как все их данные будут получены.
Это, вероятно, означает настройку промежуточной функции для управления отправкой:
func Sender(from, to int, c chan<- int) {
for i := from; i < to; i++ {
c <- i
}
}
func SendEverything(c chan<- int) {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
Sender(0, 20, c)
}()
go func() {
defer wg.Done()
Sender(11, 20, c)
}()
wg.Wait()
close(c)
}
Заставьте диспетчерскую функцию работать со всем в канале:
func Receive(c <-chan int, odds, evens chan<- int) {
for n := range c {
if n%2 == 0 {
evens <- n
} else {
odds <- n
}
}
close(odds)
close(evens)
}
И тогда вы можете поделиться одной функции печати:
func Printer(prefix string, c <-chan int) {
for n := range c {
fmt.Printf("%s: %d\n", prefix, n)
}
}
Наконец, у вас есть основная функция, которая объединяет все вместе:
func main() {
var wg sync.WaitGroup
inputs := make(chan int)
odds := make(chan int)
evens := make(chan int)
wg.Add(4)
go func() {
defer wg.Done()
SendEverything(inputs)
}()
go func() {
defer wg.Done()
Receive(inputs, odds, evens)
}()
go func() {
defer wg.Done()
Printer("odd number", odds)
}()
go func() {
defer wg.Done()
Printer("even number", evens)
}()
wg.Wait()
}
Полный пример на https://play.golang.org/p/qTUqlt-uaWH.
Обратите внимание, что я полностью воздержался от использования каких-либо глобальных переменных, и, как мы надеемся, каждое из них имеет очень понятное очень короткое имя (i
а также n
простые целые числа, c
это канал) или полные слова (odds
, evens
). Я склонен держать sync.WaitGroup
объекты локальные, где они созданы. Поскольку все передается как параметры, мне не нужны две копии одной и той же функции для работы с разными глобальными переменными, и если я решу написать тестовый код для этого, я могу создать свои собственные локальные каналы.