在Go语言中并行快速排序中的死锁问题

huangapple go评论82阅读模式
英文:

Deadlock in parallel quicksort in Go

问题

作为练习,我正在尝试在Go中实现快速排序的并行版本。这是我目前的代码:

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
}

然而,当我运行它时,我得到一个错误,声称程序已经死锁!我对导致这个问题感到困惑...

提前感谢,

Linus

英文:

As an exercise I'm trying to implement a parallel version of quicksort in Go. This is what I have so far:

func quicksort(nums []int, ch chan int, level int, threads int)  {
  level *= 2;
  if len(nums) == 1 {  ch&lt;- 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 &lt;= pivot:
      less = append(less,i)
    case i &gt; pivot:
      greater = append(greater,i)
    }
  }

  ch1 := make(chan int, len(less))
  ch2 := make(chan int, len(greater))
  if(level &lt;= 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&lt;-i;
  }
  ch&lt;-pivot
  for i := range ch2{
    ch&lt;-i;
  }
  close(ch)
  return
}

However, when I run it, I get an error claiming the program has deadlocked! I'm pretty stumped to what is causing this...

Thanks in advance,

Linus

答案1

得分: 3

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

  1. 缺少基本情况。考虑如果quicksort被要求对空切片进行排序会发生什么。
  2. 当第一次调用quicksort,比如在main()中,如果不在一个单独的goroutine中启动排序器,主线程运行顶层排序时可能会阻塞,尝试写回到通道(取决于顶层通道本身是否有缓冲区)。

例如:

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

这是有错误的,因为它要求主线程进行排序,但它最终会在quicksort的这部分代码中阻塞:它无法与自己通信!

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

在这里向顶层通道写入时,主线程将发生死锁。

与此相反,如果我们在顶层创建一个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)
    }
}

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

英文:

The code has one problem, and at least one potential buggy usage case:

  1. It is missing a base case. Consider what happens if quicksort is asked to sort the empty slice.
  2. When calling quicksort for the first time, say in a main(), if you don't spawn the sorter in a separate goroutine, the main thread, running the toplevel sort, can block trying to write back to the channel (depending if the toplevel channel itself is buffered).

For example:

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)
	}
}

This is buggy because this asks the main thread to do the sort, but it will inevitably block at this part of quicksort: it can't communicate with itself!

for i := range ch1{
	ch&lt;-i;
}

and it's at the write to the toplevel channel here that the main thread will deadlock.

Contrast this with what happens if we do create a goroutine at toplevel:

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)
    }
}

and now we avoid that particular deadlock problem.

huangapple
  • 本文由 发表于 2013年6月7日 23:20:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/16987606.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定