SoFunction
Updated on 2025-03-03

A brief analysis of how Go language achieves priority in select statements

In Go languageselectStatements are used to monitor and select a groupcaseThe statement executes the corresponding code. It looks similar toswitchstatement, butselectAll in the statementcaseThe expressions inchannelsend or receive operations. A typicalselectExamples of use are as follows

select {
case <-ch1:
	("")
case ch2 <- 1:
	("q1mi")
}

In Go languageselectKeywords can also make the currentgoroutineWaiting at the same timech1readable andch2Writable, inch1andch2Before the state changes,selectIt will keep blocking until one of themchannelExecute the corresponding state when it is converted to ready statecaseBranch code. If multiplechannelIf you are ready at the same time, select one randomlycaseimplement.

In addition to the typical examples shown above, let's introduce some of them one by one.selectSpecial examples of .

Empty select

nullselectIt means that there is no internal contentcase,For example:

select{
  
}

EmptyselectThe statement will block the currentgoroutine, so thatgoroutineEnter a permanent sleep state that cannot be awakened.

There is only one case

ifselectOnly one is includedcase, thenselectIt becomes a blockingchannelRead/write operations.

select {
case <-ch1:
	("")
}

The above code, whench1Printing is performed when readable, otherwise it will block.

There is a default statement

ifselectIt can also includedefaultStatement, used when othercasePerform some default operations when none is satisfied.

select {
case <-ch1:
	("")
default:
	()
}

The above code, whench1Printing will be performed when readable, otherwisedefaultThe code in the statement is equivalent to making a non-blockingchannelRead operation.

Summarize

  • selectNothing existscase: Permanently block the current goroutine
  • selectOnly one existscase: Blocked send/receive
  • selectThere are multiplecase: Randomly select one that meets the criteriacaseimplement
  • selectexistdefault,othercaseWhen none is satisfied: executedefaultCode in a statement

How to implement priority in select

Known, whenselectThere are multiplecaseA random selection of the conditions will be selectedcaseimplement.

Now we have a requirement: we have a function that will continue to continue fromch1andch2Receive task 1 and task 2 respectively, how to ensure thatch1andch2When 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 twoselectThe "priority" is achieved, which seems to meet the requirements of the question. But this code is a bit problematic, ifch1andch2If 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 combinationforLoop andLABELto achieve the requirements of the question. The above code is on the outer layerselectSelect Executejob2 := <-ch2When entering the inner layerselectThe loop continues to try to executejob1 := <-ch1,whench1It 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,selectThere are practical application scenarios in actual production, such asK8s controllerThere are practical examples of the above technique, here isselectComments 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 &lt;-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 &lt;-stopCh:
			return
		case nodeUpdate := &lt;-[worker]:
			(nodeUpdate)
			(nodeUpdate)
		case podUpdate := &lt;-[worker]:
			// If we find a Pod that needs to be updated, you need to clear the Node queue first.		priority:
			for {
				select {
				case nodeUpdate := &lt;-[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!