在Go中并行快速排序的死锁

时间:2013-06-07 15:20:51

标签: parallel-processing go deadlock quicksort

作为练习,我正在尝试在Go中实现并行版本的quicksort。这就是我到目前为止所做的:

func quicksort(nums []int, ch chan int, level int, threads int)  {
  level *= 2;
  if len(nums) == 1 {  ch<- nums[0]; close(ch); return }

  less := make([]int, 0)
  greater := make([]int,0)
  pivot := nums[0]
  nums = nums[1:]

  for _,i := range nums{
    switch{
    case i <= pivot:
      less = append(less,i)
    case i > pivot:
      greater = append(greater,i)
    }
  }

  ch1 := make(chan int, len(less))
  ch2 := make(chan int, len(greater))
  if(level <= threads){
    go quicksort(less, ch1, level, threads)
    go quicksort(greater,ch2, level, threads)
  }else{
    quicksort(less,ch1, level, threads)
    quicksort(greater,ch2, level, threads)
  }

  for i := range ch1{
    ch<-i;
  }
  ch<-pivot
  for i := range ch2{
    ch<-i;
  }
  close(ch)
  return
}

然而,当我运行它时,我得到一个错误声称程序已经死锁!我很难接受造成这种情况的原因......

提前致谢,

莱纳斯

1 个答案:

答案 0 :(得分:3)

代码有一个问题,至少有一个潜在的错误使用案例:

  1. 缺少一个基本案例。考虑如果要求quicksort对空切片进行排序,会发生什么。
  2. 第一次调用quicksort时,在main()中说,如果不在单独的goroutine中生成分拣机,运行顶层排序的主线程可以阻止尝试写回通道(取决于顶层通道本身是否被缓冲)。
  3. 例如:

    func main() {
        x := []int{3, 1, 4, 1, 5, 9, 2, 6}
        ch := make(chan int)
        quicksort(x, ch, 0, 0)    // buggy!
        for v := range(ch) {
            fmt.Println(v)
        }
    }
    

    这是错误的,因为这要求主线程进行排序,但它将不可避免地阻塞quicksort的这一部分:它无法与自身通信!

    for i := range ch1{
        ch<-i;
    }
    

    并且在写入toplevel通道时,主线程将死锁。

    将此与我们在顶层创建goroutine时发生的情况进行对比:

    func main() {
        x := []int{3, 1, 4, 1, 5, 9, 2, 6}
        ch := make(chan int)
        go quicksort(x, ch, 0, 0)
        for v := range(ch) {
            fmt.Println(v)
        }
    }
    

    现在我们避免了特定的死锁问题。