liveBook · Manning

User-level scheduling

As explained in Learn Concurrent Programming with Go

User-level scheduling is scheduling performed by a language runtime or user-space library rather than directly by the operating-system kernel.

User-level scheduling lets a language runtime or user-space library schedule goroutines or other user-level threads instead of relying directly on the operating-system kernel. It is usually non-preemptive: execution switches at specific points such as I/O or an explicit Go scheduler yield, unlike usually preemptive OS scheduling. section 3.3.1

The Stingy and Spendy race

Two goroutines repeatedly add to and subtract from a shared bank-account variable. On one processor, non-preemptive scheduling may make the race less visible, but parallel execution can interleave reads and writes. Runs can produce 4,203,750 or −1,127,120 instead of the expected balance, so reducing processors is not a reliable fix. section 3.3.1

How Learn Concurrent Programming with Go covers it

How deep it goes
Distinguishes non-preemptive user-level scheduling from preemptive OS scheduling and stops short of scheduler internals.
Examples
Uses Stingy and Spendy goroutines, shared money, GOMAXPROCS(1), and divergent incorrect outputs to expose scheduling effects.
What you can do after
Diagnose why single-processor execution can hide a race and recognize that scheduling changes require proper race protection.
How it connects
Connects goroutines, user-level threads, OS scheduling, preemption, context switching, I/O, yielding, and race conditions.

This explainer was generated by Manning's knowledge graph from the text of Learn Concurrent Programming with Go. The section links point to the parts of the book it draws on.