英文:
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<- 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
}
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
代码有一个问题,至少有一个潜在的错误使用情况:
- 缺少基本情况。考虑如果
quicksort
被要求对空切片进行排序会发生什么。 - 当第一次调用
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:
- It is missing a base case. Consider what happens if
quicksort
is asked to sort the empty slice. - When calling
quicksort
for the first time, say in amain()
, 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<-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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论