동시 예약

작성자

카테고리:

← 피드로
DEV Community · Alex Day · 2026-08-09 개발(SW)

Have been trying to look at 3 different languages: Go, Kotlin, Elixir/Erlang mostly to understand their concurrency models.

This article concerns mostly about Go’s preemptive scheduling. Looking at the dense article on go: https://go.googlesource.com/proposal/+/master/design/24543-non-cooperative-preemption.md

Here is what I understand.

Before Go 1.14: Cooperative preemption at function prologues

Go used compiler-inserted cooperative preemption points in function prologues up to and including Go 1.10. This means Go could only switch between concurrently-executing goroutines at specific points – and the compiler ensured that all local GC roots were known at those safe-points, enabling precise garbage collection.

The problems this caused were real and serious. In really extreme cases, it could cause a program to halt entirely.

For example, when a goroutine spinning on an atomic load starved out the goroutine responsible for setting that atomic.

They tried to fix this by inserting preemption checks at loop back-edges (the obvious next step), but even their most efficient approach – called “fault-based preemption” – added a geomean slowdown of 7.8% on a large benchmark suite.

It also had implementation downsides: it couldn’t target specific threads or goroutines, was “sticky” in that they couldn’t resume any loops until all loops were resumed, and interfered with debuggers.

Go 1.14+: Signal-based non-cooperative preemption

Non-cooperative preemption switches between concurrent execution contexts without explicit preemption checks or assistance from those contexts – the same way modern operating systems switch between threads. Without this, a single poorly-behaved goroutine can wedge a Go application, much like how a single poorly-behaved application could wedge an entire OS.

The mechanism: Go implements this by sending a POSIX signal to stop a running goroutine and capture its CPU state. If a goroutine is interrupted at a point that must be GC-atomic, the runtime simply resumes the goroutine and tries again later.

Why SIGURG specifically? It meets all the criteria: it’s passed through by debuggers by default, isn’t used internally by libc in mixed Go/C binaries, can happen spuriously without consequences, and is extremely unlikely to be used by an application for its real meaning – since out-of-band data is basically unused, and because SIGURG doesn’t report which socket has the condition, making it pretty useless for its original purpose.

SIGURG is a different beast altogether, it sits at the intersection of:

  • OS signals

  • Compiler internals

  • Runtime/GC Designs

It needs more reading. Will come back to that later.

원문에서 계속 ↗

추출 본문 · 출처: dev.to · https://dev.to/silvern47/scheduling-concurrency-9bd

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다