This article is about golang cpu affinity and how to set cpu affinity from Go on Linux: the kernel’s cpu masking for threads, and the difference from gomaxprocs golang settings. People also phrase this as set thread affinity; on Linux that is still an OS-thread property, so Go code typically combines runtime.LockOSThread with sched_setaffinity. Background on hardware threading is in CPU, cores, and threads explained; for thread counts per process, see check threads per process.
The snippets that call
sched_setaffinityortasksetare marked non-Run: they need Linux,taskseton PATH for the shell example, and a module dependency ongolang.org/x/sys/unix. They were validated in a small localgo runon Linux, not in the browser Playground.
What CPU affinity does
The scheduler may run a runnable OS thread on any allowed logical CPU. CPU affinity restricts that thread to a subset by setting a mask of allowed CPUs. That is separate from how many goroutines your program creates or how busy it is.
Gomaxprocs golang is not affinity
runtime.GOMAXPROCS(n) caps how many OS threads run user Go code at once; it does not pin those threads to specific cores. For what GOMAXPROCS interacts with, read goroutine vs threads in Go.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("NumCPU:", runtime.NumCPU())
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
}Run prints the machine’s reported CPU count and the current GOMAXPROCS limit.
Set CPU affinity with golang.org/x/sys/unix (recommended)
Use unix.CPUSet and unix.SchedGetaffinity / unix.SchedSetaffinity instead of hand-rolled syscall.RawSyscall with a uintptr mask, which uses the wrong size for the kernel cpu_set_t and is unsafe.
Save as main.go in a module, run go get golang.org/x/sys/unix, then go run . on Linux:
package main
import (
"fmt"
"runtime"
"golang.org/x/sys/unix"
)
func main() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var cur unix.CPUSet
if err := unix.SchedGetaffinity(0, &cur); err != nil {
fmt.Println("SchedGetaffinity:", err)
return
}
fmt.Println("CPUs in mask before:", cur.Count())
var set unix.CPUSet
set.Zero()
set.Set(0)
if err := unix.SchedSetaffinity(0, &set); err != nil {
fmt.Println("SchedSetaffinity:", err)
return
}
var after unix.CPUSet
if err := unix.SchedGetaffinity(0, &after); err != nil {
fmt.Println(err)
return
}
fmt.Println("pinned to CPU 0 only:", after.IsSet(0) && after.Count() == 1)
}On a multi-CPU machine you normally see more than one CPU allowed before the call, then true after restricting to CPU 0 only. Adjust set.Set for the CPUs you want; multiple Set calls build a larger cpu mask.
Set cpu affinity with taskset from Go
The taskset utility changes affinity for a PID; wrapping it with os/exec avoids cgo while still expressing set cpu affinity in shell semantics.
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
pid := fmt.Sprintf("%d", os.Getpid())
before, err := exec.Command("taskset", "-p", pid).CombinedOutput()
if err != nil {
fmt.Println(err, string(before))
return
}
fmt.Print(string(before))
out, err := exec.Command("taskset", "-cp", "0", pid).CombinedOutput()
if err != nil {
fmt.Println(err, string(out))
return
}
fmt.Print(string(out))
after, err := exec.Command("taskset", "-p", pid).CombinedOutput()
if err != nil {
fmt.Println(err, string(after))
return
}
fmt.Print(string(after))
}-cp prints and sets the mask; the exact hex mask depends on the machine. This pattern is useful for ops scripts; prefer unix.SchedSetaffinity inside pure Go libraries when you can add golang.org/x/sys.
Pitfalls: permissions, containers, and performance
- Capabilities and policy: changing another process or restrictive environments may need extra privileges; pinning the current thread as above is the common local test.
- Kubernetes and cgroups: you may be required to set allowed CPUs via the orchestrator instead of from inside the pod.
- Performance: forcing a busy CPU can hurt latency if that CPU is already hot; measure with tools such as
perforsarrather than pinning blindly.
Summary
Golang cpu affinity on Linux means applying a cpu mask to an OS thread with sched_setaffinity, most safely through golang.org/x/sys/unix and a real unix.CPUSet. Set thread affinity from Go usually adds runtime.LockOSThread so the mask applies to the right thread. gomaxprocs golang settings control parallel Go scheduling width, not which hardware threads run your process. taskset from os/exec remains a practical option for scripts and for how to set cpu affinity without writing syscalls yourself.
References
- sched_setaffinity(2)
- taskset(1)
- Package golang.org/x/sys/unix
- Stack Overflow: set CPU affinity in Go

