如何解决所有goroutines死锁
遇到所有goroutine都处于睡眠状态的问题-死锁。 我有一个包含汽车数量有限的汽车阵列的数据结构。如果线程中没有阵列,并且主线程尚未完成将数据写入数据结构,则辅助线程启动并尝试从数据结构中删除汽车,然后工作线程进入睡眠状态,直到主线程将更多汽车添加到数据结构汽车阵列中为止。然后,工作线程唤醒,从数据结构中删除汽车对象,进行计算并将其移至结果结构。在某些时候它有时会陷入僵局。请注意,即使程序无一例外地完成,仍然缺少一些数据(有时更多,有时更少)。 代码:
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io/IoUtil"
"log"
"os"
"strconv"
"sync"
)
type Car struct {
Make string `json:"Make"`
Year int `json:"Year"`
displacement float64 `json:"displacement"`
Hash string
}
type Cars struct {
Auto []Car
count int
MaxLen int
mutex *sync.Mutex
cond *sync.Cond
end bool
}
func (a *Cars) Insert(aut Car) {
a.mutex.Lock() // lock method so other thread Couldin't use Data structure
for a.count == a.MaxLen {
a.cond.Wait()//wait if current count of cars is equal to maximum amount that are allowed to store in cars array in Data structure
}
a.Auto[a.count] = aut
a.count++
a.mutex.Unlock()
a.cond.broadcast()
}
func (a *Cars) Remove(group *sync.WaitGroup) Car {
a.mutex.Lock()
for a.count == 0 {
a.cond.Wait()//if there is no cars to remove from Data struct car array then sleep
}
result := a.Auto[a.count-1]//get the last car from cars structure car array
var tmp Car
a.Auto[a.count-1] = tmp//remove the last car from structure car array
a.count--
a.mutex.Unlock() // unlock this method and let others thread use i
a.cond.broadcast() //tell all threads that removing has been finishedt
return result
}
func (a *Cars) InsertSort(aut Car) {
a.mutex.Lock()
for a.count == a.MaxLen {
a.cond.Wait()
}
j := 0
for i := 0; i < a.count; i++ {
if a.Auto[i].displacement < aut.displacement {
j = i //Finds where to insert new item in sorted list
}
}
if j != 0 {
for i := a.count; i >= j; i-- {
a.Auto[i+1] = a.Auto[i]//moves objects from j to the right
}
}
a.Auto[j] = aut
a.count++
a.mutex.Unlock()
a.cond.broadcast()
}
var Auto []Car
func main() {
CurrentWD,err := os.Getwd()
if err != nil {
log.Println(err)
}
path := CurrentWD + "\\Auto.json"
jsonFile,err := os.Open(path)
byteValue,_ := IoUtil.ReadAll(jsonFile)
json.Unmarshal(byteValue,&Auto)
var mutex = sync.Mutex{}
var cond = sync.NewCond(&mutex) //syncing cond with mutex
MaxLength := 5 // max lenght of data array
var A = make([]Car,5)
Auto1 := Cars{count: 0,MaxLen: MaxLength,cond: cond,mutex: &mutex,Auto: A}//data structs
var B = make([]Car,40)
Auto2 := Cars{count: 0,MaxLen: 40,Auto: B}//results struct
var waitGroup = sync.WaitGroup{}
ThreadsAmt := 8
waitGroup.Add(ThreadsAmt)
for i := 0; i < ThreadsAmt; i++ {
go execute(&Auto1,&waitGroup,&Auto2)
}
for _,s := range Auto {
Auto1.Insert(s)
}
Auto1.end = true//finished writing to data struct
waitGroup.Wait()
var RLoc = CurrentWD + "\\Results.txt"
f,err := os.Create(RLoc)
defer f.Close()
f.WriteString(fmt.Sprintf("%15s|%4s|%12s|%50s \n","Make","Year","displacement","Hash"))
for i := 0; i < Auto2.count-1; i++ {
f.WriteString(fmt.Sprintf("%3d %15s|%4d|%12.2f|%50s \n",i,Auto2.Auto[i].Make,Auto2.Auto[i].Year,Auto2.Auto[i].displacement,Auto2.Auto[i].Hash))
}
fmt.Println("Program finished execution")
}
func execute(Data *Cars,group *sync.WaitGroup,res *Cars) {
hash := sha256.New()
for Data.end == false && Data.count != 0 {
carTemp := Data.Remove(group)//removes and returns car object from data struct
if carTemp.displacement > 0 {//checks if returned car object displacement is bigger than *
var ss string
ss = carTemp.Make + strconv.Itoa(carTemp.Year) + fmt.Sprint(carTemp.displacement) //making string calculating hash
sum := hash.Sum([]byte(ss))
for i := 0; i < len(sum); i++ {
ss += string(sum[i])//joining hash byte array in to string
}
carTemp.Hash = ss
res.InsertSort(carTemp) // inserts car
}
}
defer group.Done()
}
数据:Auto.json
[{
"Make": "Chrysler","Year": 1997,"displacement": 3.6
},{
"Make": "Honda","Year": 2016,"displacement": 1.4
},{
"Make": "Aston Martin","Year": 2009,"displacement": 4.1
},{
"Make": "Geo","Year": 2011,"displacement": 4.9
},{
"Make": "Buick","Year": 2001,"displacement": 6.3
},{
"Make": "Chevrolet","displacement": 2.7
},{
"Make": "Suzuki","Year": 2004,"displacement": 4.5
},{
"Make": "Studebaker","displacement": 7.5
},"Year": 2020,"displacement": 1.1
},{
"Make": "Volkswagen","Year": 1996,"displacement": 6.2
},{
"Make": "Mercedes-Benz","displacement": 2.9
},{
"Make": "Nissan","Year": 2019,"displacement": 7.2
},{
"Make": "Subaru","Year": 2010,"displacement": 2.6
},{
"Make": "Hummer","Year": 1991,"displacement": 8.8
},"Year": 2017,"displacement": 8.0
},{
"Make": "Mitsubishi","displacement": 6.6
},"displacement": 2.0
},{
"Make": "Lincoln","displacement": 9.9
},"Year": 1998,"displacement": 3.4
},{
"Make": "Dodge","displacement": 5.8
},{
"Make": "GMC","displacement": 6.8
},"Year": 2013,{
"Make": "Ford","displacement": 5.1
},{
"Make": "Toyota","displacement": 9.6
},{
"Make": "Hyundai","Year": 2015,"displacement": 3.8
},"displacement": 4.3
},"displacement": 2.2
},"displacement": 1.8
},{
"Make": "Pontiac","Year": 2006,"displacement": 4.6
},"Year": 2008,"displacement": 9.2
}]
goroutine 1 [sync.Cond.Wait]:
runtime.goparkunlock(...)
E:/Program Files (x86)/Go projects/go1.15.2/src/runtime/proc.go:312
sync.runtime_notifyListWait(0xc00003c050,0x3)
E:/Program Files (x86)/Go projects/go1.15.2/src/runtime/sema.go:513 +0x117
sync.(*Cond).Wait(0xc00003c040)
E:/Program Files (x86)/Go projects/go1.15.2/src/sync/cond.go:56 +0xa5
main.(*Cars).Insert(0xc00003c080,0xc000014343,0x3,0x7e0,0x401b333333333333,0x0,0x0)
c:/Users/Justas/OneDrive - Kaunas University of Technology/5
Semestras/Lygiagretusis programavimas/Lab1/main.go:32 +0x57
main.main()
c:/Users/Justas/OneDrive - Kaunas University of Technology/5
Semestras/Lygiagretusis programavimas/Lab1/main.go:109 +0x53c
exit status 2
Process exiting with code: 1 signal: false
解决方法
欢迎使用StackOverflow&Go开发! Go货币功能强大-但也很难掌握。不要气disc!
问题:
- 而其他函数采用
sync
锁定-execute()
函数未锁定-导致大量数据争用情况- 编译数据争用版本
go build --race
,并且标准错误将显示并发读写的位置。阅读more here。
- 编译数据争用版本
-
InsertSort
函数在很多情况下都坏了 - 使用哈希器不正确:
- 创建哈希,写入字节,然后通过
h.Sum(nil)
计算哈希
- 散列是二进制的-因此,当使用
fmt
打印它们时,建议使用十六进制格式。 (%x
)
- 创建哈希,写入字节,然后通过
设计:
- 正如@Adrian提到的那样-在输入工作项时,使用通道更容易进行协调。在这里切片很难控制并发访问
- 您的输出需要一个排序的切片-因此在这种情况下,结果通道没有意义。
- Go切片已经具有可以使用本机
len()
函数检索的计数-因此无需使用您自己的count
字段跟踪长度
减少切片的长度时, - 切片条目不需要为零
- 要删除切片的最后一个元素:
s = s[:len(s)-1]
- 要删除切片的最后一个元素:
- 切片操作过于怪异,无法确定死锁的原因
- go标准库有
sort.Slice
为您完成繁重的工作
我应用了以上所有建议,并将它们滚动到基于通道的解决方案中-输出切片上带有互斥锁,以便在运行时对结果进行排序:
https://play.golang.org/p/ewR3zHxirL8
可以通过使工作程序将结果写入输出通道来改善此问题-并且使goroutine读取结果,从而使结果仅在末尾进行一次排序。这样就不需要任何sync.Mutex
-以及任何自定义结构:
https://play.golang.org/p/0T1fFaP0iml
,对不起,我没有详细介绍。但是,立即引起注意的一件事是数据争用,您需要在运行Data.end
的goroutine中检查execute()
并在Auto1.end = true
中用main()
对其进行修改。同样,Data.Count
的使用不受互斥量保护。
我注意到的另一个错误是在InsertSort
中,如果位移小于第一个元素的位移,则j
为零,而新元素替换了现有元素。这也许可以解释您丢失的数据。
您是否尝试过使用种族探测器?这对于发现问题可能很有用,其中一些问题可能导致死锁。但是,这个问题听起来像它更适合使用通道(例如大多数并发问题)。以我的经验,使用互斥锁使事情变得更加困难。
还有许多其他小事情可以整理,以使代码更易于理解。例如,未使用Remove()
方法的参数,defer group.Done()
应该位于execute()
函数的顶部。也许您可以解决上述问题,对其进行测试,然后在仍然存在问题的情况下发布新代码。您可以尝试做的一件事是在关键点添加日志消息,以尝试了解正在发生的事情。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。