所有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都在睡眠中-死锁!

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()函数的顶部。也许您可以解决上述问题,对其进行测试,然后在仍然存在问题的情况下发布新代码。您可以尝试做的一件事是在关键点添加日志消息,以尝试了解正在发生的事情。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...