2016-11-04 10 views
1

В следующем коде я пытаюсь создать MaxOutstanding количество обработчиков. Каждый обработчик перебирает элементы в очереди queue и распечатывает их, я также пишу true на канал done.goroutine asleep - deadlock

В моей основной функции я запускаю обработчики и записываю 9 элементов в queue и жду, пока 1-й элемент будет записан в очередь done.

package main 


import "fmt" 

type Request struct { 
     int32 
} 
var MaxOutstanding = 5 
func handle(queue chan *Request, i int, done chan bool) { 
    for r := range queue { 
     fmt.Println(i, "---", r) 
     done <- true 
    } 
} 

func Serve(clientRequests chan *Request, quit, done chan bool) { 
    // Start handlers 
    for i := 0; i < MaxOutstanding; i++ { 
     go handle(clientRequests, i, done) 
    } 
    <-quit // Wait to be told to exit. 
} 


func main() { 
    clientRequests := make(chan *Request) 
    quit := make(chan bool) 
    done := make(chan bool) 

    go Serve(clientRequests, quit, done) 

    clientRequests <- &Request{4} 
    clientRequests <- &Request{1} 
    clientRequests <- &Request{2} 
    clientRequests <- &Request{3} 

    clientRequests <- &Request{5} 
    clientRequests <- &Request{6} 
    clientRequests <- &Request{7} 
    clientRequests <- &Request{8} 
    clientRequests <- &Request{9} 
    fmt.Println("...........>", <- done) 
    close(clientRequests) 
    close(done) 
} 

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

4 --- &{4} 
0 --- &{1} 
1 --- &{2} 
2 --- &{3} 
3 --- &{5} 
fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [chan send]: 
main.main() 
     /home/ubuntu/digs-svc/src/digs/go1.go:45 +0x251 

goroutine 5 [chan receive]: 
main.Serve(0xc82004c060, 0xc82004c0c0, 0xc82004c120) 
     /home/ubuntu/digs-svc/src/digs/go1.go:28 +0x92 
created by main.main 
     /home/ubuntu/digs-svc/src/digs/go1.go:37 +0xb9 

goroutine 6 [chan send]: 
main.handle(0xc82004c060, 0x0, 0xc82004c120) 
     /home/ubuntu/digs-svc/src/digs/go1.go:16 +0x23b 
created by main.Serve 
     /home/ubuntu/digs-svc/src/digs/go1.go:25 +0x5b 

EDIT:

Видимо, fmt.Println("....", <- done) не было достаточно, чтобы показать, что есть потребитель в done канале. Я переместил код в порядке выполнения. Потребитель должен «слушать» канал, когда данные записываются на него. В моем предыдущем коде, когда были записаны первые данные, потребителя не было.

Рабочий код.

https://play.golang.org/p/98l2M4XO9t

+1

На первый взгляд вы пишете ' сделал 'не менее 5 раз, но прочитал его только один раз. – myaut

+0

@myaut, Это намерение. Меня интересует только первый экземпляр записываемых данных. – Pratyush

ответ

1

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

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

func handle(queue chan *Request, i int, wg *sync.WaitGroup) { 
    defer wg.Done() 

    for r := range queue { 
     fmt.Println(i, "---", r) 
    } 
} 

func Serve(clientRequests chan *Request, wg *sync.WaitGroup) { 
    // Start handlers 
    for i := 0; i < MaxOutstanding; i++ { 
     wg.Add(1) 
     go handle(clientRequests, i, wg) 

    } 
} 

func main() { 
    clientRequests := make(chan *Request) 
    var wg sync.WaitGroup 

    go Serve(clientRequests, &wg) 

    for i := int32(0); i < 50; i++ { 
     clientRequests <- &Request{i} 
    } 

    close(clientRequests) 
    wg.Wait() 
} 

https://play.golang.org/p/oUFjZONjhk (обратите внимание, что на детской площадке, этот пример, кажется, в настоящее время выступает один goroutine являющегося приемника. Обычно блокированные goroutines будут получать случайный образ, и вы можете увидеть, что поведение при компиляции и работать в нормальном режиме)

+0

Спасибо за ответ с WaitGroup. Я думаю, что это лучшее решение для синхронизации нескольких goroutines. – Pratyush

+0

«Потому что ничего не получается с другой стороны» В соответствии с кодом была 1 строка, которая фактически получала данные с другой стороны 'fmt.Println (" ...........> ", <- done) '. Я переместил строку в порядке выполнения, и она работала нормально.Я полагаю, что канал должен иметь потребителя во время записи данных, а не в будущем. – Pratyush

+0

@Pratyush: да, было 1 получение, но вам нужно постоянно получать, чтобы те, которые были на петлях. – JimB

1

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

Чтобы преодолеть эту ситуацию, вы можете отправить значение запроса внутри для цикла:

for i := 0; i < MaxOutstanding; i++ { 
     clientRequests <- &Request{int32(i)} 
} 

Вот рабочий код:

package main 

import (
    "fmt" 
) 

type Request struct { 
    int32 
} 

var MaxOutstanding = 10 

func handle(queue chan *Request, i int, done chan bool) { 
    for r := range queue { 
     fmt.Println(i, "---", r) 
     done <- true 
    } 
} 

func Serve(clientRequests chan *Request, quit, done chan bool) { 
    // Start handlers 
    for i := 0; i < MaxOutstanding; i++ { 
     go handle(clientRequests, i, done) 
    } 
    <-quit // Wait to be told to exit. 
} 

func main() { 
    clientRequests := make(chan *Request) 
    quit := make(chan bool) 
    done := make(chan bool) 

    go Serve(clientRequests, quit, done) 
    for i := 0; i < MaxOutstanding; i++ { 
     clientRequests <- &Request{int32(i)} 
    } 

    fmt.Println("...........>", <-done) 
    close(clientRequests) 
    close(done) 
} 

https://play.golang.org/p/L5Y2YoFNvz

+1

Если я изменяю цикл for в основной функции на 'for i: = 0; i <100; i ++ 'Я получаю ошибку тупика - https://play.golang.org/p/rT2aa-sllX – Pratyush

+1

Я переместил код, чтобы слить в очередь' done' в порядке выполнения, и, похоже, он работает нормально. https://play.golang.org/p/UxCu1220-- – Pratyush

+1

@Pratyush: на самом деле не работает с окончательным '<-done' в конце, потому что вы мчитесь в другой тупик: https: //play.golang .org/P/_hzsC3LKIs – JimB