Stage 1 · Code
Concurrency in Practice
The Context Package
Concurrent work needs a clean stop signal when the caller gives up. `context.Context` is the standard way Go carries cancellation, deadlines, and request-scoped metadata through a whole call chain.
Why Context Travels with Work
Imagine an HTTP request arrives asking your service to build a report. The handler starts a database query. That query starts a goroutine to gather extra data. Then the client disconnects or the deadline expires. If none of that downstream work hears about the cancellation, your program keeps burning CPU, network, and database time on a result nobody can use anymore.
That is the problem context.Context solves. A context is a value you pass down a call chain so the whole tree of work can share the same stop signal and time limits. The most important thing to understand first is not the interface methods. It is the ownership story: the caller decides how long it is willing to wait, and deeper code is expected to respect that decision.
By convention, any function that might block, do I/O, or start cancellable work takes ctx context.Context as its first parameter. That keeps the caller in control all the way down the stack.
Cancellation and `Done`
A context carries several things, but cancellation is the part you will use first and most often. context.WithCancel(parent) creates a child context and a cancel function. When you call cancel(), the child context is marked done, and its Done() channel is closed. Any goroutine selecting on <-ctx.Done() can wake up and exit.
Two small habits matter here. First, always call the cancel function you receive from WithCancel, WithTimeout, or WithDeadline. Second, check ctx.Err() when you exit because it tells you whether the reason was a manual cancellation or a deadline being exceeded.
Timeouts and Deadlines
Manual cancellation is useful when some higher-level event says stop. Time limits are the automatic version. context.WithTimeout says cancel this after this much time from now. context.WithDeadline says cancel this at this exact time.
| Constructor | What you provide | Best mental model |
|---|---|---|
context.WithCancel | Just a parent context | Stop when I explicitly say stop |
context.WithTimeout | A duration like 200 * time.Millisecond | Stop after this much waiting |
context.WithDeadline | A concrete time.Time | Stop at this wall-clock moment |
Values Are Not Optional Parameters
context.WithValue exists, but it solves a narrow problem. It is for request-scoped metadata that should travel with the request, such as a request ID, authenticated user information, or tracing data. It is not a general-purpose bag for optional function parameters.
A good rule is simple: if a piece of data is really part of the function's behavior, put it in the function signature. Reserve WithValue for metadata that travels alongside the request rather than steering the business logic itself.
Quiz and Practice
Why is `context.Context` usually passed as the first parameter?
Hands-On Project
This exercise focuses on the core skill: waiting for work, but exiting early when the caller cancels or the deadline expires.
Write a function `WaitOrCancel(ctx context.Context, work time.Duration) string` that waits for `work` to finish. If the timer finishes first, return `done`. If `ctx.Done()` happens first, return `cancelled: <ctx.Err()>`.
Summary and Key Takeaways
context.Contextlets the caller control cancellation and time limits across a whole tree of work.- Pass context as the first parameter to functions that may block or launch cancellable work.
ctx.Done()is the channel you wait on;ctx.Err()tells you why the work stopped.- Use
WithCancelfor manual stop signals,WithTimeoutfor relative limits, andWithDeadlinefor exact cutoffs. - Use
WithValuesparingly for request-scoped metadata, not as a hidden bag of optional parameters.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.