0
0
Goprogramming~10 mins

Common concurrency patterns in Go - Step-by-Step Execution

Choose your learning style9 modes available
Concept Flow - Common concurrency patterns
Start Goroutine
Perform Task Concurrently
Communicate via Channel
Synchronize with WaitGroup
Collect Results
Main Goroutine Continues
This flow shows how Go runs tasks concurrently using goroutines, communicates results through channels, and synchronizes completion with WaitGroups.
Execution Sample
Go
package main
import (
 "fmt"
 "sync"
)
func main() {
 var wg sync.WaitGroup
 ch := make(chan string)
 wg.Add(1)
 go func() {
  defer wg.Done()
  ch <- "Hello from goroutine"
 }()
 go func() {
  wg.Wait()
  close(ch)
 }()
 for msg := range ch {
  fmt.Println(msg)
 }
}
This Go program starts a goroutine that sends a message through a channel, waits for it to finish, then closes the channel so the main goroutine can print the message.
Execution Table
StepActionGoroutine StateChannel StateWaitGroup CounterOutput
1Main starts, creates WaitGroup and channelMain runningEmpty0
2Main adds 1 to WaitGroupMain runningEmpty1
3Start goroutine to send messageGoroutine startedEmpty1
4Goroutine sends "Hello from goroutine" to channelGoroutine blocked on sendEmpty (send blocked)1
5Main starts goroutine to wait and close channelWait goroutine startedEmpty (send blocked)1
6Main reads from channelMain receives from channelEmpty after read1Hello from goroutine
7Goroutine finishes, calls Done()Goroutine finishedEmpty0
8Wait goroutine detects WaitGroup counter 0, closes channelWait goroutine finishedClosed0
9Main loop ends as channel closedMain finishedClosed0
💡 Channel closed after WaitGroup counter reached zero, main loop ends.
Variable Tracker
VariableStartAfter Step 2After Step 4After Step 6After Step 7Final
WaitGroup counter011100
Channel stateemptyemptyemptyemptyemptyclosed
Output"""""""Hello from goroutine""Hello from goroutine""Hello from goroutine"
Key Moments - 3 Insights
Why does the goroutine block when sending to the channel at step 4?
Because the channel is unbuffered and no goroutine is ready to receive yet, the send operation waits until the main goroutine reads from the channel (step 6).
Why do we need a separate goroutine to wait and close the channel?
Closing the channel after all sends are done signals the main goroutine to stop reading. The wait goroutine waits for WaitGroup counter to reach zero before closing, ensuring no sends happen after close.
What happens if we forget to call wg.Done() in the sending goroutine?
The WaitGroup counter never decreases, so the wait goroutine never closes the channel, causing the main goroutine to block forever waiting for more messages.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the WaitGroup counter after step 7?
A0
B1
C2
DUndefined
💡 Hint
Check the 'WaitGroup Counter' column at step 7 in the execution table.
At which step does the main goroutine receive the message from the channel?
AStep 4
BStep 6
CStep 8
DStep 9
💡 Hint
Look at the 'Output' column to see when the message appears.
If the channel was buffered with capacity 1, how would step 4 change?
AGoroutine would block waiting for receiver
BChannel would close immediately
CGoroutine would send without blocking
DWaitGroup counter would increase
💡 Hint
Buffered channels allow one send without waiting for a receiver.
Concept Snapshot
Go concurrency uses goroutines to run tasks at the same time.
Channels let goroutines send and receive messages safely.
WaitGroups help wait for many goroutines to finish.
Unbuffered channels block senders until receivers are ready.
Closing a channel signals no more messages will come.
Use these patterns to coordinate concurrent work safely.
Full Transcript
This visual execution trace shows how Go uses common concurrency patterns: starting goroutines, communicating via channels, and synchronizing with WaitGroups. The main goroutine creates a WaitGroup and a channel, then starts a goroutine that sends a message through the channel. Because the channel is unbuffered, the sending goroutine blocks until the main goroutine reads the message. Another goroutine waits for the WaitGroup counter to reach zero and then closes the channel, signaling the main goroutine to stop reading. The variable tracker shows how the WaitGroup counter and channel state change step by step. Key moments clarify why blocking happens on unbuffered channels, why closing channels is done after all sends, and the importance of calling Done on the WaitGroup. The quiz tests understanding of WaitGroup counters, message receipt timing, and buffered channel behavior. This pattern is essential for safe and effective concurrent programming in Go.