9 Concurrency: Practice

 

This chapter covers

  • Preventing common mistakes with goroutines and channels
  • Understanding the impacts of using standard data structures alongside concurrent code
  • Using the standard library and some extensions
  • Avoiding data races and deadlocks

In the previous chapter, we discussed the foundations of concurrency. Now it’s time to look at practical mistakes made by Go developers when working with the concurrency primitives.

9.1 #61: Propagating an inappropriate context

Contexts are omnipresent when working with concurrency in Go, and in many situations, it may be recommended to propagate them. However, context propagation can sometimes lead to subtle bugs, preventing subfunctions from being correctly executed.

Let’s consider the following example. We expose an HTTP handler that performs some tasks and returns a response. But just before returning the response, we also want to send it to a Kafka topic. We don’t want to penalize the HTTP consumer latency-wise, so we want the publish action to be handled asynchronously within a new goroutine. We assume that we have at our disposal a publish function that accepts a context so the action of publishing a message can be interrupted if the context is canceled, for example. Here is a possible implementation:

9.2 #62: Starting a goroutine without knowing when to stop it

9.3 #63: Not being careful with goroutines and loop variables

9.4 #64: Expecting deterministic behavior using select and channels

9.5 #65: Not using notification channels

9.6 #66: Not using nil channels

9.7 #67: Being puzzled about channel size

9.8 #68: Forgetting about possible side effects with string formatting

9.8.1 etcd data race

9.8.2 Deadlock

9.9 #69: Creating data races with append

9.10 #70: Using mutexes inaccurately with slices and maps