Context, propagation, and cancellation patterns in Go vs Java | Patterns, idioms, and best practices of Go
1. Context and its role
In Go context.Context is used to pass cancellation signals and timeouts between goroutine, as well as to pass values (propagation). For Java developers, this is somewhat similar to Future with cancellation, Thread.interrupt() and ThreadLocal.
// Go: basic context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go func(ctx context.Context) {
select {
case <-time.After(5 * time.Second):
fmt.Println("Goroutine completed")
case <-ctx.Done():
fmt.Println("Goroutine cancelled:", ctx.Err())
}
}(ctx)
// Java: cancellation using Future and ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> f = executor.submit(() -> {
try {
Thread.sleep(5000);
System.out.println("Task completed");
} catch (InterruptedException e) {
System.out.println("Task cancelled");
}
});
f.cancel(true); // cancellation
executor.shutdown();
Context helps to pass cancellation signals and timeouts between goroutine, which is especially important in parallel and network tasks.
2. Context propagation
Values can be passed through context so that all child goroutines receive shared information without global variables.
// Go: passing value through context
ctx := context.WithValue(context.Background(), "userID", 42)
go func(ctx context.Context) {
fmt.Println("UserID:", ctx.Value("userID"))
}(ctx)
// Java: ThreadLocal for context passing
ThreadLocal<Integer> userID = ThreadLocal.withInitial(() -> 42);
Runnable task = () -> System.out.println("UserID: " + userID.get());
new Thread(task).start();
In Go, context propagation simplifies the passing of data and cancellation signals through the goroutine hierarchy instead of global variables or complex parameter passing.
3. Cancellation patterns
The context allows flexible cancellation of tasks. Main approaches:
- timeout / deadline
- explicit cancellation through
cancel() - combination with select for non-blocking wait
// Go: combined example of fan-in with cancellation
func worker(ctx context.Context, jobs <-chan int, results chan<- int) {
for {
select {
case j, ok := <-jobs:
if !ok { return }
results <- j * 2
case <-ctx.Done():
return
}
}
}
ctx, cancel := context.WithCancel(context.Background())
jobs := make(chan int)
results := make(chan int)
go worker(ctx, jobs, results)
// Cancellation
cancel()
Use context for safe cancellation of all related goroutines, especially in fan-in/fan-out schemes.
4. Comparison Table Go vs Java
| Concept | Go | Java | Comment |
|---|---|---|---|
| Task Cancellation | context with cancel(), timeouts, and deadline | Future.cancel(), Thread.interrupt() | Go allows safe and centralized cancellation of all child goroutines |
| Data Passing | context.WithValue() passes values through the hierarchy of goroutines | ThreadLocal or method parameters | In Go, there are fewer global variables, safe in concurrent tasks |
| Fan-in / Fan-out | goroutine + channels + select + context for cancellation | ExecutorService + BlockingQueue + Future + manual cancellation | Go makes the pattern lighter and more manageable |
| Timeouts | context.WithTimeout / WithDeadline | Future.get(timeout), ScheduledExecutor | The context provides a single way to set timeouts for the entire chain of goroutines |
Summary
The use of context.Context is the key to safe and manageable concurrency in Go. It combines three tasks:
- Data passing between goroutines (propagation)
- Task cancellation (cancellation)
- Timeouts and deadlines
For Java developers, the context is similar to a combination of ThreadLocal + Future/interrupt + timeout. Go provides a more direct, unified, and safe management mechanism, especially in fan-in/fan-out schemes.
Оставить комментарий
Useful Articles:
New Articles: