所有 goroutine 在我的异步代码中都处于休眠状态

我读了这个,这个和这个,但没有一个能解决我的问题..


我正在尝试异步读取 2 个文件,所以我写了以下内容:


//readlines.go

package main


import (

    "bufio"

    "os"

)


// readLines reads a whole file into memory

// and returns a slice of its lines.

func readLines(path string) ([]string, error) {

    file, err := os.Open(path)

    if err != nil {

        return nil, err

    }

    defer file.Close()


    var lines []string

    scanner := bufio.NewScanner(file)

    for scanner.Scan() {

        lines = append(lines, scanner.Text())

    }

    return lines, scanner.Err()

}

并将其称为:


package main


import (

    "fmt"

    "os"


    "github.com/gocarina/gocsv"

)


func (s *stocks) Read() {

    fmt.Println("Reading")

    stockFile, err := os.OpenFile("current_invenory.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)

    if err != nil {

        panic(err)

    }

    defer stockFile.Close()

    stocks := []systemStock{}

    if err := gocsv.UnmarshalFile(stockFile, &stocks); err != nil { // Load stocks from file

        panic(err)

    }


    *s = stocks

}


package main


import (

    "fmt"

    "os"


    "github.com/gocarina/gocsv"

)


func (t *transactions) Read() {

    fmt.Println("Reading")

    trxFile, err := os.OpenFile("current_transactions.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)

    if err != nil {

        panic(err)

    }

    defer trxFile.Close()

    trx := []systemTransactions{}

    if err := gocsv.UnmarshalFile(trxFile, &trx); err != nil { // Load stocks from file

        panic(err)

    }


    *t = trx

}


以上工作非常好:


    stock := stocks{} 

    trx := transactions{}


    stock.Read()

    trx.Read()

    for _, s := range stock {

            fmt.Println("Hello", s.Code)

    }

但是fatal error: all goroutines are asleep - deadlock!当我尝试将它们读取为时给出错误:


    cs, ct := readData()


    for _, s := range cs {

        fmt.Println("Hello", s.Code)

    }


    for _, t := range ct {

        fmt.Println("Hello trx of ", t.Code)

    }



所以这个错误与我在最后一个块中犯的错误(或不明白)有关~


米琪卡哇伊
浏览 128回答 1
1回答

森栏

为了同时运行这些Read方法,这些方法需要有一种在它们完成执行时发出信号的方式。这可以通过多种方式完成,但这里有两种需要对代码进行最少修改的方式。stockstransactions解决方案 1使用sync.WaitGroup包。使用这个包,Read方法在执行完成后应该执行wg.Done()语句。它应该看起来像这样:func (s *stocks) Read(wg *sync.WaitGroup) {    defer wg.Done()    fmt.Println("Reading")    stockFile, err := os.OpenFile("current_invenory.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)    if err != nil {        panic(err)    }    defer stockFile.Close()    stocks := []systemStock{}    if err := gocsv.UnmarshalFile(stockFile, &stocks); err != nil { // Load stocks from file        panic(err)    }    *s = stocks}func (t *transactions) Read(wg *sync.WaitGroup) {    defer wg.Done()    fmt.Println("Reading")    trxFile, err := os.OpenFile("current_transactions.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)    if err != nil {        panic(err)    }    defer trxFile.Close()    trx := []systemTransactions{}    if err := gocsv.UnmarshalFile(trxFile, &trx); err != nil { // Load stocks from file        panic(err)    }    *t = trx}func readData() (stocks, transactions) {    var wg sync.WaitGroup    wg.Add(2)    stock := stocks{}    trx := transactions{}    go stock.Read(&wg)    go trx.Read(&wg)    wg.Wait()    return stock, trx}解决方案 2这种方法使用golang.org/x/sync/errgroup包。在这种情况下,您不需要自己处理同步和信令,但是使用errgroup.Go方法添加的函数需要具有严格的func() error签名。您的代码应如下所示:func (s *stocks) Read() error {        fmt.Println("Reading")        stockFile, err := os.OpenFile("current_invenory.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)        if err != nil {            return err        }        defer stockFile.Close()        stocks := []systemStock{}        if err := gocsv.UnmarshalFile(stockFile, &stocks); err != nil { // Load stocks from file            return err        }            *s = stocks        return nil    }        func (t *transactions) Read() error {        fmt.Println("Reading")        trxFile, err := os.OpenFile("current_transactions.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)        if err != nil {            return err        }        defer trxFile.Close()        trx := []systemTransactions{}        if err := gocsv.UnmarshalFile(trxFile, &trx); err != nil { // Load stocks from file            return err        }            *t = trx        return nil    }        func readData() (stocks, transactions) {        g, _ := errgroup.WithContext(context.Background())            stock := stocks{}        trx := transactions{}            g.Go(stock.Read)        g.Go(trx.Read)            if err:= g.Wait(); err != nil {           panic(err)        }            return stock, trx    }解决方案 3当您开始从每个 CSV 读取时,您(正确地)将 1 添加到等待组,使等待组的内部计数器变为 2,但是 wg.Wait() 将等到该计数器下降到零并且您没有任何调用 wg.Done() 来做到这一点。我建议将 go stock.Read() 更改为:go func() {    defer wg Done()    stock.Read()}()因此,完整的工作代码是:func readData() (stocks, transactions) {    var wg sync.WaitGroup    stock := stocks{}    trx := transactions{}    wg.Add(1)    go func() {        defer wg.Done()        stock.Read()    }()    wg.Add(1)    go func() {        defer wg.Done()        trx.Read()    }()    wg.Wait()    return stock, trx}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go