Go

Why doesn't Go expose goroutine IDs, and how do you "kill" a goroutine?

Question 165MediumGo 1.22 to 1.25

The Go team left out goroutine IDs on purpose. With IDs, people would build goroutine-local storage, which hides dependencies and breaks as soon as work moves to another goroutine, for example a pool. Instead, you pass request-scoped values explicitly through context.Context. The runtime does have an internal goid, visible in stack traces. Hacks that parse runtime.Stack to get it exist but are discouraged.

You cannot kill a goroutine from outside. Forcibly stopping one would leave locks held and invariants broken. Cancellation is cooperative: the goroutine has to check for it.

func worker(ctx context.Context, jobs <-chan Job) error {
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case j, ok := <-jobs:
			if !ok {
				return nil
			}
			if err := handle(ctx, j); err != nil {
				return err
			}
		}
	}
}

For long CPU loops, check ctx.Err() every N iterations. For blocking I/O, set deadlines or close the connection or file, which unblocks the pending call. runtime.Goexit ends only the calling goroutine, after running its deferred calls.

More on Goroutines & the Scheduler

All 35 Goroutines & the Scheduler questions