Golang中的select语句,为什么在我添加了default分支后会出现死锁?

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

Golang,Select, why select deadlock when I add default?

问题

以下是死锁的代码:

package main

import (
	"sync"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(2)

	a := make(chan int)

	go func() {
		defer wg.Done()
		for {
			var (
				name string
				x    int
				ok   bool
			)

			select {
			case x, ok = <-a:
				name = "a"
			default:
				println("default error")
			}

			if !ok {
				return
			}

			println(name, x, ok)
		}

	}()
	go func() {
		defer wg.Done()
		defer close(a)

		for i := 0; i < 10; i++ {
			select {
			case a <- i:
			}
		}
	}()

	wg.Wait()
}

输出结果为:

a 0 true
default error
fatal error: all goroutines are asleep - deadlock!

但是当我从select中移除default时,一切正常。

package main

import (
	"sync"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(2)

	a := make(chan int)

	go func() {
		defer wg.Done()
		for {
			var (
				name string
				x    int
				ok   bool
			)

			select {
			case x, ok = <-a:
				name = "a"
			}

			if !ok {
				return
			}

			println(name, x, ok)
		}

	}()
	go func() {
		defer wg.Done()
		defer close(a)

		for i := 0; i < 10; i++ {
			select {
			case a <- i:
			}
		}
	}()

	wg.Wait()
}

输出结果为:

a 0 true
a 1 true
a 2 true
a 3 true
a 4 true
a 5 true
a 6 true
a 7 true
a 8 true
a 9 true

a 0 true
default error
fatal error: all goroutines are asleep - deadlock!

a 0 true
a 1 true
a 2 true
a 3 true
a 4 true
a 5 true
a 6 true
a 7 true
a 8 true
a 9 true

英文:

The below code is deadlock.

package main

import (
	&quot;sync&quot;
)

func main() {
	var wg sync.WaitGroup
	wg.Add(2)

	a := make(chan int)

	go func() {
		defer wg.Done()
		for {
			var (
				name string
				x    int
				ok   bool
			)

			select {
			case x, ok = &lt;-a:
				name = &quot;a&quot;
			default:
				println(&quot;default error&quot;)
			}

			if !ok {
				return
			}

			println(name, x, ok)
		}

	}()
	go func() {
		defer wg.Done()
		defer close(a)

		for i := 0; i &lt; 10; i++ {
			select {
			case a &lt;- i:
			}
		}
	}()

	wg.Wait()
}

a 0 true
default error
fatal error: all goroutines are asleep - deadlock!

but when I remove default from select, everything is well.

package main

import (
	&quot;sync&quot;
)

func main() {
	var wg sync.WaitGroup
	wg.Add(2)

	a := make(chan int)

	go func() {
		defer wg.Done()
		for {
			var (
				name string
				x    int
				ok   bool
			)

			select {
			case x, ok = &lt;-a:
				name = &quot;a&quot;
			}

			if !ok {
				return
			}

			println(name, x, ok)
		}

	}()
	go func() {
		defer wg.Done()
		defer close(a)

		for i := 0; i &lt; 10; i++ {
			select {
			case a &lt;- i:
			}
		}
	}()

	wg.Wait()
}

a 0 true
a 1 true
a 2 true
a 3 true
a 4 true
a 5 true
a 6 true
a 7 true
a 8 true
a 9 true

a 0 true
default error
fatal error: all goroutines are asleep - deadlock!

a 0 true
a 1 true
a 2 true
a 3 true
a 4 true
a 5 true
a 6 true
a 7 true
a 8 true
a 9 true

答案1

得分: 1

当你在select语句中添加default子句,并且如果该select在第二个goroutine的通道发送操作之前运行,那么第一个goroutine将返回并终止。这是因为使用default时,第一个select仅仅选择默认情况,因为通道还没有准备好,此时ok被初始化为false,所以goroutine返回。由于现在没有goroutine从通道中读取数据,第二个goroutine被阻塞,wg.Wait也被阻塞,因此发生了死锁。

英文:

When you add the default clause to the select statement, and if that select runs before the channel send operation of the second goroutine, the first goroutine returns and terminates. This is because with the default the first select simply selects the default case because the channel is not ready, at which point ok is initialized to false, so the goroutine returns. Since now there is no goroutine reading from the channel, the second goroutine blocks, and wg.Wait also blocks, hence deadlock.

huangapple
  • 本文由 发表于 2023年1月15日 12:45:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75122986.html
匿名

发表评论

匿名网友

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

确定