multithreading - 所有goroutines死锁

标签 multithreading go deadlock goroutine thread-sleep

遇到所有 goroutine 都处于休眠状态的问题 - 死锁。
我有一个带有汽车数组的数据结构,其中的汽车数量有限。如果数组中没有汽车并且主线程尚未完成将数据写入数据结构,则工作线程启动并尝试从数据结构中删除汽车,然后工作线程休眠,直到主线程将更多汽车添加到数据结构汽车数组中。然后工作线程唤醒,从数据结构中删除汽车对象,进行计算并将其移动到结果结构中。在某些时候,它有时会陷入僵局。注意到即使程序毫无异常(exception)地完成,一些数据(有时更多,有时更少)仍然丢失。
代码:

    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, cond: cond, mutex: &mutex, 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",
  "Year": 2001,
  "Displacement": 2.7
}, {
  "Make": "Suzuki",
  "Year": 2004,
  "Displacement": 4.5
}, {
  "Make": "Studebaker",
  "Year": 2001,
  "Displacement": 7.5
}, {
  "Make": "Chevrolet",
  "Year": 2020,
  "Displacement": 1.1
}, {
  "Make": "Volkswagen",
  "Year": 1996,
  "Displacement": 6.2
}, {
  "Make": "Mercedes-Benz",
  "Year": 2009,
  "Displacement": 2.9
}, {
  "Make": "Nissan",
  "Year": 2019,
  "Displacement": 7.2
}, {
  "Make": "Subaru",
  "Year": 2010,
  "Displacement": 2.6
}, {
  "Make": "Hummer",
  "Year": 1991,
  "Displacement": 8.8
}, {
  "Make": "Subaru",
  "Year": 2017,
  "Displacement": 8.0
}, {
  "Make": "Mitsubishi",
  "Year": 2010,
  "Displacement": 6.6
}, {
  "Make": "Mercedes-Benz",
  "Year": 1996,
  "Displacement": 2.0
}, {
  "Make": "Lincoln",
  "Year": 1991,
  "Displacement": 9.9
}, {
  "Make": "Chevrolet",
  "Year": 1998,
  "Displacement": 3.4
}, {
  "Make": "Dodge",
  "Year": 2010,
  "Displacement": 5.8
}, {
  "Make": "GMC",
  "Year": 2016,
  "Displacement": 6.8
}, {
  "Make": "Chevrolet",
  "Year": 2013,
  "Displacement": 3.4
}, {
  "Make": "Ford",
  "Year": 2010,
  "Displacement": 5.1
}, {
  "Make": "Toyota",
  "Year": 2017,
  "Displacement": 9.6
}, {
  "Make": "Hyundai",
  "Year": 2015,
  "Displacement": 3.8
}, {
  "Make": "Mercedes-Benz",
  "Year": 2016,
  "Displacement": 4.3
}, {
  "Make": "Chevrolet",
  "Year": 2019,
  "Displacement": 2.2
}, {
  "Make": "Dodge",
  "Year": 2009,
  "Displacement": 1.8
}, {
  "Make": "Pontiac",
  "Year": 2006,
  "Displacement": 4.6
}, {
  "Make": "Chevrolet",
  "Year": 2008,
  "Displacement": 9.2
}]
错误:
fatal error :所有 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 开发!围棋货币很强大——但也很难掌握。不要气馁!
问题:

  • 而其他函数使用 sync 锁定 - execute() 函数没有 - 导致大量数据竞争条件
  • 编译数据竞争版本 go build --race 和标准错误将显示并发读/写发生的位置。阅读 more here

  • InsertSort 功能在很多边缘情况下被破坏
  • 使用哈希不正确:
  • 创建哈希,写入字节,然后通过 h.Sum(nil) 计算哈希
  • 散列是二进制的 - 因此在使用 fmt 十六进制格式打印它们时,建议使用。 ( %x )


  • 设计:
  • 正如@Adrian 提到的 - 使用 channel 更容易协调 - 在输入工作项目时。 slice 在这里控制并发访问很乏味
  • 您的输出需要排序 slice - 因此在这种情况下,结果 channel 没有意义。
  • Go slice 已经有一个计数,您可以使用 native len() 函数检索 - 因此无需使用您自己的 count 字段
  • 跟踪长度
  • 减少 slice 长度时, slice 条目不需要清零
  • 删除 slice 的最后一个元素:s = s[:len(s)-1]

  • 有太多古怪的 slice 操作来确定死锁的原因
  • 去标准库有 sort.Slice 为你做繁重的工作

  • 我应用了上述所有建议并将它们滚动到基于 channel 的解决方案中 - 在输出 slice 上使用互斥锁,以便在运行时对结果进行排序:
    https://play.golang.org/p/ewR3zHxirL8
    这可以通过让工作人员将结果写入输出 channel 来改善 - 并让结果 goroutine 读取该 channel ,因此在最后只排序一次。这消除了对任何 sync.Mutex 和任何自定义结构的需要:
    https://play.golang.org/p/0T1fFaP0iml

    关于multithreading - 所有goroutines死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64234386/

    相关文章:

    Java.util.Timer - 主线程结束时继续处理

    multithreading - 线程不在 Netty UDP 服务器中同时执行

    c++ - 我如何在 C++11 中实现类似 "interrupted exception"行为的 Java

    go - 从 emersion/go-imap 获取原始消息

    c++ - 线程构建 block : Deadlocks because all threads used up

    c# - .net 中的死锁示例?

    Java 对象监视器

    go - 如何构造SELECT * FROM (<subquery>) ORDER BY column;形式的子查询?

    Go SDK Apache Beam : singleton side input Singleton for int ill-defined

    java - 当指定多个条件锁定时会出现死锁