Hard15 minGo Fundamentals
UpdatedAug 4, 2026
Edit

Goroutines and Channels

CONCEPTS:Go Goroutines and Channels

Question Variations

  • "What is the difference between a goroutine and an operating-system thread?"
  • "When does a channel send block?"
  • "Who should close a channel?"
  • "How do you prevent a goroutine leak?"

Why This Is Asked

Go concurrency is a core part of the language’s design. Interviewers use this question to assess whether you can coordinate concurrent work safely, explain channel blocking behavior, and avoid goroutine leaks.

Key Concepts

  • A go statement starts a function call in a new goroutine.
  • Unbuffered channel sends and receives synchronize; buffered channels allow limited decoupling.
  • Closing a channel signals that no more values will be sent.
  • Context cancellation and bounded concurrency are essential for long-running services.

Question Variations

  • “What is the difference between a goroutine and an operating-system thread?”
  • “When does a channel send block?”
  • “Who should close a channel?”
  • “How do you prevent a goroutine leak?”

Answers by Technology

+ Add Variant
GoImprove this answer ✏️

Expected Answer (Go 1.26.5)

A goroutine is a lightweight concurrent function managed by the Go runtime, not a one-to-one operating-system thread. Channels communicate typed values between goroutines. An unbuffered channel send waits for a receiver, while a buffered channel send waits only when its buffer is full.

The sending side should generally close a channel, and only when it is certain no further sends will occur. Receivers should use cancellation to stop work when a caller no longer needs the result.

package main

import (
	"context"
	"fmt"
)

func produce(ctx context.Context, values chan<- int) {
	defer close(values)
	for i := 1; i <= 3; i++ {
		select {
		case values <- i:
		case <-ctx.Done():
			return
		}
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	values := make(chan int)
	go produce(ctx, values)
	for value := range values {
		fmt.Println(value)
	}
}

Why It Matters

Services commonly fan work out to goroutines for requests, background jobs, and I/O. Without clear ownership, cancellation, and channel lifecycles, they can deadlock, leak resources, or overwhelm downstream systems.

Common Mistakes

  • Closing a channel from the receiving side: A concurrent sender can panic by sending on the closed channel.
  • Assuming a goroutine automatically stops when its caller returns: It continues until it returns or is otherwise coordinated.
  • Using an unbounded goroutine per input item: High load can exhaust memory or downstream capacity; bound concurrency.

Follow-up Questions

  • What does receiving from a closed channel return? (Answer: Remaining buffered values, then the zero value with ok set to false.)
  • When is a mutex preferable to a channel? (Answer: When protecting shared state is simpler than transferring ownership or coordinating a workflow.)