Голанг: Получение «фатальной ошибки: все goroutines спят - тупик» на waitGroup.Wait () - PullRequest
0 голосов
/ 21 февраля 2019

Я пытаюсь написать код, который выполняет одновременное чтение файла и отправляет содержимое на один канал.

Здесь - ссылка на мой код и код:

func main() {
    bufferSize := int64(10)
    f, err := os.Open("tags-c.csv")
    if err != nil {
        panic(err)
    }
    fileinfo, err := f.Stat()
    if err != nil {
        fmt.Println(err)
        return
    }
    filesize := int64(fileinfo.Size())
    fmt.Println(filesize)
    routines := filesize / bufferSize
    if remainder := filesize % bufferSize; remainder != 0 {
        routines++
    }
    fmt.Println("Total routines : ", routines)

    channel := make(chan string, 10)
    wg := &sync.WaitGroup{}

    for i := int64(0); i < int64(routines); i++ {
        wg.Add(1)
        go read(i*bufferSize, f, channel, bufferSize, filesize, wg)

    }
    fmt.Println("waiting")
    wg.Wait()
    fmt.Println("wait over")
    close(channel)

    readChannel(channel)
}

func readChannel(channel chan string) {
    for {
        data, more := <-channel
        if more == false {
            break
        }
        fmt.Print(data)
    }
}

func read(seek int64, file *os.File, channel chan string, bufferSize int64, filesize int64, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("read :: ", seek)
    var buf []byte
    if filesize < bufferSize {
        buf = make([]byte, filesize)
    } else if (filesize - seek) < bufferSize {
        buf = make([]byte, filesize-seek)
    } else {
        buf = make([]byte, bufferSize)
    }

    n, err := file.ReadAt(buf, seek)
    if err != nil {
        log.Printf("loc %d err: %v", seek, err)
        return
    }
    if n > 0 {
        channel <- string(buf[:n])
        fmt.Println("ret :: ", seek)
    }
}

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

1 Ответ

0 голосов
/ 21 февраля 2019

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

И канал буферизуется, который может содержать до 10элементы.Как только 10 goroutines отправят на него сообщение, остальные будут заблокированы, поэтому они никогда не будут завершены (так как чтение с этого канала может начаться только после того, как все они вернутся: это тупик).

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

done := make(chan struct{})
go readChannel(channel, done)

fmt.Println("waiting")
wg.Wait()
fmt.Println("wait over")
close(channel)

// Wait for completion of collecting the results:
<-done

Где чтение канала должно быть for range (которое заканчивается, когда канал закрыт, и все значения, полученные от него, были полученыотправлено на него до того, как оно было закрыто):

func readChannel(channel chan string, done chan struct{}) {
    for data := range channel {
        fmt.Print(data)
    }
    close(done)
}

Обратите внимание, что я использовал канал done, поэтому основная программа также будет ожидать завершения выполнения процедуры получения результатов.

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

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