A tip per issue: Uncertain select statement in go

About the select statement

  • In the select statement, case does not depend on the order in which the code is written.
  • If there is a message in one of the cases, other cases/default will not be executed.
  • If there are multiple messages in the case, randomly select one to execute, and the others will not execute
  • If all cases have no news and contain the default branch at the same time, the default branch will be taken
  • If there is no message in all cases and there is no default branch, it will block and wait for the return message in the case to continue execution
func g1(ch chan int) {
    
    
	ch <- 42
}

func g2(ch chan int) {
    
    
	ch <- 43
}

func main() {
    
    

	ch1 := make(chan int)
	ch2 := make(chan int)

	go g1(ch1)
	go g2(ch2)

	select {
    
    
	case v1 := <-ch1:
		fmt.Println("Got:";, v1)
	case v2 := <-ch2:
		fmt.Println("Got", v2)
	default:
		fmt.Println("The default case!")
	}
}

Since the execution order of goroutine is uncertain, it is not sure whether to output 42 43 or The default case!, you can use the following script to execute 5000 times to see (save the following script as for.sh, and then chmod a +x for.sh, followed by ./for.sh)

rm -f for.txt
touch for.txt
for i in `seq 5000`
do
  go build -race main.go
  ./main >> for.txt
  rm -f main
done

Then look at the output in for.txt, most of them are default, and a small part is 42 and 43. So he will still choose the case first to execute, but we don't write this kind of select. Once we write it, we will probably have to pay for this uncertainty

So why does this Xiongtai's code must be default?

func AsyncServices() chan int {
    
    
	ch := make(chan int, 1)
	go func() {
    
    
		time.Sleep(time.Millisecond * 200) // 不管有没有这个走的都是default
		ch <- 1
	}()
	return ch
}

func TestSelect(t *testing.T) {
    
    
	select {
    
    
	case ch := <-AsyncServices():
		t.Log(ch)
	case <-time.After(time.Millisecond * 100):
		t.Error("Time out.")
	default:
		t.Log("Receive nothing.")
	}
}

This select can determine how to execute each case. In general, the AsyncServices() function call is time-consuming and will definitely be slower than default. If we change the above code to the following:

so

func main() {
    
    
	ch := make(chan int, 1)
	go func() {
    
    
		ch <- 1
	}()
	select {
    
    
	case ch := <-ch:
		fmt.Println(ch)
	case <-time.After(time.Millisecond * 100):
		fmt.Println("Time out";)
	default:
		fmt.Println("Receive nothing")
	}
}

or this

func Test_ab(t *testing.T) {
    
    
	ch := make(chan int)

	go func() {
    
    
		ch <- 1
	}()
	
	time.Sleep(1000)
	select {
    
    
	case res1 := <-ch:
		fmt.Println("case1: ", res1)
	//case <-time.After(time.Millisecond * 100):
	//	fmt.Println("timeout")
	//case res2 := <-ch2:
	//	fmt.Println("case2: ", res2)
	default:
		fmt.Println("default: ")
	}
}

Essentially, before the code enters select, let the channel return data as much as possible

Note: The essence of select lies in uncertainty. If you are forced to fix it to a certain case, please think about why you use select?

Guess you like

Origin blog.csdn.net/qq_33709508/article/details/132309213