Почему есть фатальная ошибка: все горутины спят - тупик!в этом коде? - PullRequest
0 голосов
/ 03 октября 2018

Это относится к следующему коду на языке программирования Go - глава 8, с.238, скопированному ниже из этой ссылки

// makeThumbnails6 makes thumbnails for each file received from the channel.
// It returns the number of bytes occupied by the files it creates.
func makeThumbnails6(filenames <-chan string) int64 {
    sizes := make(chan int64)
    var wg sync.WaitGroup // number of working goroutines
    for f := range filenames {
        wg.Add(1)
        // worker
        go func(f string) {
            defer wg.Done()
            thumb, err := thumbnail.ImageFile(f)
            if err != nil {
                log.Println(err)
                return
            }
            info, _ := os.Stat(thumb) // OK to ignore error
            fmt.Println(info.Size())
            sizes <- info.Size()
        }(f)
    }

    // closer
    go func() {
        wg.Wait()
        close(sizes)
    }()

    var total int64
    for size := range sizes {
        total += size
    }
    return total
}

Почему нам нужно приблизитьгорутин?Почему не может работать ниже?

// closer
        // go func() {
        fmt.Println("waiting for reset")
                wg.Wait()
        fmt.Println("closing sizes")
                close(sizes)
        // }()

Если я пытаюсь запустить выше кода, это дает:

ожидание сброса
3547
2793
фатальная ошибка: все рутины спят - тупик!

Почему выше тупик?К вашему сведению, в методе, который вызывает makeThumbnail6 я закрываю filenames канал

1 Ответ

0 голосов
/ 03 октября 2018

Ваш канал не буферизован (вы не указали размер буфера при создании () канала.Это означает, что запись в канал блокируется до считывания записанного значения.И вы читаете из канала после вашего вызова wg.Wait (), так что ничто никогда не будет прочитано, и все ваши подпрограммы застрянут при блокирующей записи.

Тем не менее, вам здесь не нужна WaitGroup.WaitGroups хороши, когда вы не знаете, когда закончится ваша процедура, но вы отправляете результаты обратно, так что вы знаете.Вот пример кода, который аналогичен тому, что вы пытаетесь сделать (с фальшивым рабочим грузом).

package main

import (
    "fmt"
    "time"
)

func main() {
    var procs int = 0
    filenames := []string{"file1", "file2", "file3", "file4"}
    mychan := make(chan string)
    for _, f := range filenames {
        procs += 1
        // worker
        go func(f string) {
            fmt.Printf("Worker processing %v\n", f)
            time.Sleep(time.Second)
            mychan <- f
        }(f)
    }

    for i := 0; i < procs; i++ {
        select {
        case msg := <-mychan:
            fmt.Printf("got %v from worker channel\n", msg)
        }
    }
}

Протестируйте его на игровой площадке здесь https://play.golang.org/p/RtMkYbAqtGO

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...