Thoughts on go select deadlock

Time:2022-5-5

Thoughts on go select deadlock

https://mp.weixin.qq.com/s/Ov1FvLsLfSaY8GNzfjfMbg Continuous thinking triggered by an article

Summary above

Summary I

package main

import (
 "fmt"
)

func main() {
 ch := make(chan int)
 go func() {
  select {
  case ch

No matter which case is finally selected by select, getval () will execute in the order of source code: getval (1) and getval (2), that is, they must output first:

getVal, i= 1
getVal, i= 2

Summary II

package main

import (
 "fmt"
 "time"
)

func talk(msg string, sleep int)

Each time you enter the following select statement:

select {
case ch

And will be executed, and the corresponding values are: a x and B x (where x is 0-5). However, only one of the cases will be selected for execution each time, so the result of sum must be discarded, that is, it will not be written into ch. Therefore, a total of only 5 times will be output, and the other 5 times will be lost. (you will find that among the five output results, X is 0 1 2 3 4, for example)

In main, there are 10 cycles and only 5 results are obtained, so a deadlock is reported after 5 times of output.

If you change to this, everything is normal:

select {
case t :=

My understanding:
The case ch statement is executed in two sections, which can be understood as

t :=

Extension of the problem

Mentioned above
No matter which case is finally selected by select, getval () will execute in the order of source code: getval (1) and getval (2), that is, they must output first:

getVal, i= 1
getVal, i= 2

Think 1:If the execution time of getval () method is different, does the running time of select depend on the running time or the sum of the running time?

func getVal1(i int) int {
	time.Sleep(time.Second * 1)
	fmt.Println("getVal, i=", i)
	return i
}
func getVal2(i int) int {
	time.Sleep(time.Second * 2)
	fmt.Println("getVal, i=", i)
	return i
}

func main() {
	ch := make(chan int)
	go func() {
		for {
			beginTime := time.Now()
			select {
			case ch

Output results

getVal, i= 1
getVal, i= 2
3.0015862s
getVal, i= 1
getVal, i= 2
3.0021938s
getVal, i= 1
getVal, i= 2
3.0019246s

It can be seen that each select will execute the case statements in order, and the execution time of the select is the sum of the case statements
Of course, there will be no such writing in actual production
Correct writing:

func main() {
	begin := time.Now()
	ch := make(chan int)
	ch2 := make(chan int, 2)
	go func() {
		ch2

Output results, depending on the longest runninggetVal()

getVal, i= 1
1
getVal, i= 2
2
2.0020979s

In actual production, the select statement is only used to accept the value in the channel, not to execute a method

Careful friends have found that there are two bugs in the above writing

  1. In a new start-up process, the for statement causes it to idle all the time, and the process will not be destroyed
  2. If you send data to ch after it is closed, panic will result

Add some comments to see the output

func main() {
	begin := time.Now()
	ch := make(chan int)
	ch2 := make(chan int, 2)
	go func() {
		ch2

Output results

getVal, i= 1
getVal, i= 2
goroutine num 2
1
2
2.0020965s
goroutine num 2
panic err send on closed channel

It can be seen that the coroutine of the for loop is not released, and it will be used in the following stepsPanic exception is also reported in Ch operation