Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

How Go Schedules Goroutines: G-M-P, Work Stealing, and What GOMAXPROCS Really Means

Go schedules many goroutines across OS threads using Ps as execution resources. Learn how G-M-P and work stealing work, what GOMAXPROCS controls, and why Go 1.25 changed the default.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Go runs many goroutines across operating-system threads, but a thread needs a runtime resource called a P before it can execute Go code. The number of Ps equals the current GOMAXPROCS value, so GOMAXPROCS limits how many threads can execute Go code simultaneously—not how many goroutines or OS threads a program can have. Since Go 1.25, the default can also account for container CPU limits, rather than simply reflecting the machine’s logical CPU count.

How does the Go scheduler work?

The runtime’s job is to distribute ready-to-run goroutines across worker threads. The common G-M-P model is a useful way to understand how it does that:

As an Amazon Associate I earn from qualifying purchases.

Letter Resource What it represents What to remember
G Goroutine A unit of Go work that can be runnable, running, waiting, or otherwise not ready to run. Programs can have many more goroutines than can run at once.
M Operating-system thread A thread on which the runtime executes work. An M needs a P to execute Go code, but it can be blocked in a system call without holding a P.
P Runtime processor resource The runtime resources needed to execute Go code, including scheduler and memory allocator state. The runtime has as many Ps as the current GOMAXPROCS value. A P is not a physical CPU core.

“M:N” is shorthand for multiplexing many goroutines over a set of OS threads. It is incomplete on its own: the P explains what limits simultaneous Go execution and why goroutine count, thread count, and execution capacity are different quantities.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What happens when work blocks?

A goroutine that waits—for example, for a channel operation or other event—cannot use an execution slot while it is waiting. The runtime can schedule other runnable work instead. An M blocked in a system call can exist without a P, allowing the P to be used for other Go work. This is why neither the number of goroutines nor the number of OS threads directly tells you how many goroutines are executing Go code at a given moment.

How does work stealing work in Go?

The runtime maintains distributed scheduler state, including work queues associated with Ps. If a worker has no local work, the scheduler can look for runnable goroutines or timer work associated with another P. In the current runtime source, stealWork attempts to steal runnable goroutine or timer work from any P.

Work stealing helps the runtime find work when runnable tasks are unevenly distributed. It does not guarantee a particular goroutine order, a fixed fairness bound, or queues of equal length. Those are not promises developers should build application behavior around.

Why does the runtime park and wake workers?

Keeping every worker active can waste CPU when there is little useful work; keeping too many workers parked can delay work that becomes runnable. The runtime balances worker parking and waking against those competing costs. Its scheduler state is distributed, and future work cannot be known perfectly, so the exact worker behavior is an implementation detail rather than a stable scheduling contract.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What does GOMAXPROCS actually control?

The Go runtime documentation defines GOMAXPROCS as the maximum number of CPUs that can be executing simultaneously. In the G-M-P model, that capacity corresponds to the number of Ps: an M needs a P to run Go code, and the runtime has as many Ps as the current GOMAXPROCS setting.

For example, the Go blog uses GOMAXPROCS=8 and 1,000 runnable goroutines to illustrate that Go can use eight threads to run eight goroutines at a time. Those numbers are an explanatory example, not a benchmark. The important distinction is that runnable goroutines can outnumber simultaneous Go execution slots.

Does GOMAXPROCS limit goroutines or OS threads?

No. It sets Go execution parallelism, not a goroutine quota or a cap on all OS threads. A program may have many goroutines waiting or ready to run, and the runtime may have OS threads that are idle or blocked in system calls. Only an M with a P can execute Go code.

Nor does a P represent a reserved physical core. GOMAXPROCS describes the runtime’s available parallelism; it does not reserve CPU time or guarantee a share of the host processor. Container CPU quotas and operating-system scheduling still affect how much CPU time a process actually receives and the latency it experiences.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How did Go’s default GOMAXPROCS change in Go 1.25?

From Go 1.5 through Go 1.24, the default was based on the machine’s logical CPU count. Go 1.25 introduced a container-aware default intended to account better for CPU limits. Current runtime documentation describes the default as potentially considering logical CPU count, process CPU affinity, and—on Linux—the process’s average CPU throughput limit based on its cgroup quota. The runtime can periodically update its default when relevant conditions change.

Go version context Default behavior Practical implication
Go 1.5–1.24 Based on the machine’s logical CPU count. A container could see a value based on host CPU availability rather than its CPU quota.
Go 1.25 and later, according to current runtime documentation Can consider logical CPUs, process affinity, and Linux cgroup CPU throughput limits; the default can be updated periodically as relevant conditions change. The default may better reflect the process’s effective CPU environment, but it does not reserve that CPU capacity.

The Go blog describes the intended meaning this way: “Semantically, GOMAXPROCS tells the Go runtime the ‘available parallelism’ that Go should use.” For a containerized application, the default therefore depends on more than the host’s advertised CPU count.

What if GOMAXPROCS is set explicitly?

Setting GOMAXPROCS through the environment or calling runtime.GOMAXPROCS explicitly disables automatic updates to the default, according to the runtime package documentation. The package also documents runtime.SetDefaultGOMAXPROCS as a way to restore default behavior.

Compatibility settings matter as well. The current documentation describes GODEBUG=containermaxprocs=0 and GODEBUG=updatemaxprocs=0; their defaults are tied to language version 1.24 and earlier. Check the runtime documentation for the Go version and language version your program uses before relying on their behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How should you choose or check GOMAXPROCS?

Do not hard-code a value solely because it matches the host’s logical CPU count, or assume every container needs a manual override. First identify the Go version, any explicit configuration, and the process’s CPU environment:

  1. Check the Go version. Determine whether the program uses Go 1.25 or later, when container-aware defaults were introduced.
  2. Check for an override. Look for the GOMAXPROCS environment variable, calls to runtime.GOMAXPROCS, and relevant GODEBUG settings. An explicit setting disables automatic default updates.
  3. Check the process’s CPU constraints. Establish whether CPU affinity or, on Linux, a cgroup quota affects the process. These can matter to the default.
  4. Measure the application under its real workload. Compare performance and latency in the environment where the program runs before changing parallelism. A setting that suits one workload or deployment may not suit another.

How can you inspect scheduler activity?

The Go performance wiki documents two scheduler trace settings:

  • GODEBUG=schedtrace=1000 prints a scheduler trace every 1,000 milliseconds.
  • GODEBUG=schedtrace=1000,scheddetail=1 requests detailed scheduler output at the same interval.

Trace output can expose values such as GOMAXPROCS, idle processors, worker threads, global run-queue length, and local per-P queues. Use it as evidence to investigate a workload, not as a diagnosis by itself: queue and worker counts need to be interpreted alongside application measurements and other profiling information.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.