Все горутины спят - PullRequest
       2

Все горутины спят

0 голосов
/ 06 ноября 2018

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

    var counter int64  // shared resource

    var wg sync.WaitGroup

    func main() {
        ch := make(chan int64)

        wg.Add(2)

        go incCounter(ch)
        go incCounter(ch)

        ch <- counter

        wg.Wait()
        fmt.Println("Final Counter:", counter) // expected value is 4
    }

    func incCounter(ch chan int64) {
        defer wg.Done()

        for count := 0; count < 2; count++ {
            value := <-ch
            value++
            counter = value
            ch <- counter
        }
    }

Когда я запустил эту программу, произошла ошибка: all goroutines are asleep - deadlock!. Однако я не могу решить проблему, и я не знаю, что не так. Может ли кто-нибудь помочь?

1 Ответ

0 голосов
/ 06 ноября 2018

Каналы make(chan int) имеет неявный размер ноль (ref: https://golang.org/ref/spec#Making_slices_maps_and_channels)

Канал нулевого размера не буферизован. Канал указанного размера make (chan int, n) буферизуется. См. http://golang.org/ref/spec#Send_statements для обсуждения каналов с буферизацией и без буферизации. Пример на http://play.golang.org/p/VZAiN1V8-P иллюстрирует разницу.

Здесь канал <-ch или ch <- будет заблокирован, пока кто-нибудь не обработает его ( одновременно ). Если вы попробуете эту программу ручкой и бумагой, вы поймете, почему она заблокирована. На рисунке ниже показан возможный поток данных по каналу ch:

enter image description here

Так что если вы сделаете ваш ch := make(chan int64) до ch := make(chan int64,1), он будет работать.

var counter int64 // shared resource
var wg sync.WaitGroup

func main() {
    ch := make(chan int64, 1)

    wg.Add(2)

    go incCounter(ch)
    go incCounter(ch)

    ch <- counter

    wg.Wait()
    fmt.Println("Final Counter:", counter) // expected value is 4
}

func incCounter(ch chan int64) {
    defer wg.Done()

    for count := 0; count < 2; count++ {
        value := <-ch
        value++
        counter = value
        ch <- counter
    }
}

Если мы проанализируем, как работает программа, когда вы используете ch := make(chan int64), мы увидим, что одна подпрограмма go заблокирована в этой программе (другая закрыта). С помощью time.Sleep(n) и получения последних данных из канала в процедуре заблокированного перехода мы можем преодолеть тупик. Смотрите код ниже:

var counter int64 // shared resource
var wg sync.WaitGroup

func main() {
    ch := make(chan int64)

    wg.Add(2)

    go incCounter(ch)
    go incCounter(ch)

    ch <- counter

    // to ensure one go routine 'incCounter' is completed and one go routine is blocked for unbuffered channel
    time.Sleep(3*time.Second)

    <-ch // to unblock the last go routine

    wg.Wait()
    fmt.Println("Final Counter:", counter) // expected value is 4
}

func incCounter(ch chan int64) {
    defer wg.Done()

    for count := 0; count < 2; count++ {
        value := <-ch
        value++
        counter = value
        ch <- counter
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...