Golang中所有goroutine都处于休眠状态 - 死锁问题!
Golang中所有goroutine都处于休眠状态 - 死锁问题! 我阅读了这个、这个和这个,但它们都没有解决我的问题。
我尝试异步读取两个文件,所以我写了下面的代码:
//readlines.go
package main
import (
"bufio"
"os"
)
// readLines 将整个文件读入内存
// 并返回其行的切片。
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 { // 从文件加载库存
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 { // 从文件加载库存
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)
}
使用了
import "sync"
//func readData(cs chan stocks, ct chan transactions) (stocks, transactions) {
func readData() (stocks, transactions) {
var wg sync.WaitGroup
defer wg.Done()
stock := stocks{}
trx := transactions{}
wg.Add(1)
go stock.Read()
wg.Add(1)
go trx.Read()
wg.Wait()
return stock, trx
}
所以这个错误与我最后一块代码中做错(或不理解)的某些事情有关。
更多关于Golang中所有goroutine都处于休眠状态 - 死锁问题!的实战教程也可以访问 https://www.itying.com/category-94-b0.html
嗨,@hyousef,你在开始读取每个CSV文件时(正确地)向等待组添加了1,这使得等待组内部计数器变为2,但wg.Wait()会一直等待,直到该计数器降为零,而你没有任何调用wg.Done()来减少它。我建议将go stock.Read()改为:
go func() {
defer wg.Done()
stock.Read()
}()
然后对交易记录的处理也做同样的修改。
更多关于Golang中所有goroutine都处于休眠状态 - 死锁问题!的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你的代码存在几个关键问题导致了死锁。主要问题是:
defer wg.Done()位置错误:它在函数开始时就被调用,而不是在goroutine结束时- 缺少通道同步:goroutine修改了共享变量,但没有适当的同步机制
- WaitGroup使用不当:
wg.Done()应该在每个goroutine结束时调用
以下是修正后的代码:
import "sync"
func readData() (stocks, transactions) {
var wg sync.WaitGroup
var mu sync.Mutex
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
}
或者使用通道来避免数据竞争:
func readData() (stocks, transactions) {
var wg sync.WaitGroup
stockChan := make(chan stocks, 1)
trxChan := make(chan transactions, 1)
wg.Add(1)
go func() {
defer wg.Done()
stock := stocks{}
stock.Read()
stockChan <- stock
}()
wg.Add(1)
go func() {
defer wg.Done()
trx := transactions{}
trx.Read()
trxChan <- trx
}()
wg.Wait()
close(stockChan)
close(trxChan)
return <-stockChan, <-trxChan
}
如果你需要保持原始的方法签名不变,但修复并发问题:
func readData() (stocks, transactions) {
var wg sync.WaitGroup
var mu sync.Mutex
stock := stocks{}
trx := transactions{}
wg.Add(2)
go func() {
defer wg.Done()
mu.Lock()
stock.Read()
mu.Unlock()
}()
go func() {
defer wg.Done()
mu.Lock()
trx.Read()
mu.Unlock()
}()
wg.Wait()
return stock, trx
}
死锁发生的原因是:defer wg.Done()在函数开始时执行,立即减少了WaitGroup的计数器,然后wg.Wait()发现计数器已经为0,直接返回。但此时两个goroutine还没有开始执行,主goroutine继续执行到return语句,而两个后台goroutine试图修改共享变量,但没有同步机制,导致运行时检测到所有goroutine都处于休眠状态。

