In Go languageselect
Statements are used to monitor and select a groupcase
The statement executes the corresponding code. It looks similar toswitch
statement, butselect
All in the statementcase
The expressions inchannel
send or receive operations. A typicalselect
Examples of use are as follows
select { case <-ch1: ("") case ch2 <- 1: ("q1mi") }
In Go languageselect
Keywords can also make the currentgoroutine
Waiting at the same timech1
readable andch2
Writable, inch1
andch2
Before the state changes,select
It will keep blocking until one of themchannel
Execute the corresponding state when it is converted to ready statecase
Branch code. If multiplechannel
If you are ready at the same time, select one randomlycase
implement.
In addition to the typical examples shown above, let's introduce some of them one by one.select
Special examples of .
Empty select
nullselect
It means that there is no internal contentcase
,For example:
select{ }
Emptyselect
The statement will block the currentgoroutine
, so thatgoroutine
Enter a permanent sleep state that cannot be awakened.
There is only one case
ifselect
Only one is includedcase
, thenselect
It becomes a blockingchannel
Read/write operations.
select { case <-ch1: ("") }
The above code, whench1
Printing is performed when readable, otherwise it will block.
There is a default statement
ifselect
It can also includedefault
Statement, used when othercase
Perform some default operations when none is satisfied.
select { case <-ch1: ("") default: () }
The above code, whench1
Printing will be performed when readable, otherwisedefault
The code in the statement is equivalent to making a non-blockingchannel
Read operation.
Summarize
-
select
Nothing existscase
: Permanently block the current goroutine -
select
Only one existscase
: Blocked send/receive -
select
There are multiplecase
: Randomly select one that meets the criteriacase
implement -
select
existdefault
,othercase
When none is satisfied: executedefault
Code in a statement
How to implement priority in select
Known, whenselect
There are multiplecase
A random selection of the conditions will be selectedcase
implement.
Now we have a requirement: we have a function that will continue to continue fromch1
andch2
Receive task 1 and task 2 respectively, how to ensure thatch1
andch2
When the ready state is reached at the same time, priority will be given to task 1, and then task 2 will be executed when there is no task 1?
High-level Go language programmer Xiao Ming scratched his head and wrote the following function:
func worker(ch1, ch2 <-chan int, stopCh chan struct{}) { for { select { case <-stopCh: return case job1 := <-ch1: (job1) default: select { case job2 := <-ch2: (job2) default: } } } }
The above code is nested by twoselect
The "priority" is achieved, which seems to meet the requirements of the question. But this code is a bit problematic, ifch1
andch2
If none of them reach the ready state, the entire program will not block but will enter a dead loop.
What to do?
Xiao Ming scratched his head again and wrote another solution:
func worker2(ch1, ch2 <-chan int, stopCh chan struct{}) { for { select { case <-stopCh: return case job1 := <-ch1: (job1) case job2 := <-ch2: priority: for { select { case job1 := <-ch1: (job1) default: break priority } } (job2) } } }
This time, Xiao Ming not only used nestedselect
, also used in combinationfor
Loop andLABEL
to achieve the requirements of the question. The above code is on the outer layerselect
Select Executejob2 := <-ch2
When entering the inner layerselect
The loop continues to try to executejob1 := <-ch1
,whench1
It will be executed when it is ready, otherwise it will jump out of the inner layer.select
。
Practical application scenarios
Although I compiled the above requirements,select
There are practical application scenarios in actual production, such asK8s controllerThere are practical examples of the above technique, here isselect
Comments have been added to the key points of the implementation of priority-related codes, so I will not elaborate on the specific logic here.
// kubernetes/pkg/controller/nodelifecycle/scheduler/taint_manager.go func (tc *NoExecuteTaintManager) worker(worker int, done func(), stopCh <-chan struct{}) { defer done() // When handling specific events, we will hope that the Node update operation takes precedence over the Pod update // Because NodeUpdates has nothing to do with NoExecuteTaintManager, it should be handled as soon as possible // -- We do not want users (or systems) to wait until the PodUpdate queue is exhausted before starting to clear pods from the contaminated Node. for { select { case <-stopCh: return case nodeUpdate := <-[worker]: (nodeUpdate) (nodeUpdate) case podUpdate := <-[worker]: // If we find a Pod that needs to be updated, you need to clear the Node queue first. priority: for { select { case nodeUpdate := <-[worker]: (nodeUpdate) (nodeUpdate) default: break priority } } // We will process podUpdate after the Node queue is cleared. (podUpdate) (podUpdate) } } }
This is the end of this article about a brief analysis of how Go language can achieve priority in select statements. For more relevant Go select priority content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!