Introduction to Concurrency in Go
Concurrency is one of Go’s most powerful features and a key reason why Go is widely used for backend systems, cloud platforms, and high-performance servers.
In this lesson, you will understand what concurrency is, why it matters, and how Go approaches concurrency differently from many other programming languages.
What Is Concurrency?
Concurrency means handling multiple tasks at the same time. These tasks may not run at exactly the same instant, but they make progress independently and overlap in execution.
For example:
- Handling multiple HTTP requests on a server
- Processing user input while loading data
- Downloading files while updating the UI
Concurrency vs Parallelism
Concurrency and parallelism are related but not the same.
- Concurrency: Managing multiple tasks at once
- Parallelism: Executing multiple tasks at the same exact time
Concurrency is about structure and design, while parallelism depends on hardware (multiple CPU cores).
Real-World Analogy
Imagine a restaurant kitchen:
- A single chef cooking multiple dishes by switching between tasks is concurrency
- Multiple chefs cooking different dishes at the same time is parallelism
Go is designed to handle both efficiently.
Why Go Is Excellent for Concurrency
Go was designed with concurrency as a first-class feature. Instead of complex thread management, Go provides lightweight concurrency tools.
- Simple syntax
- Low memory overhead
- Scales efficiently
- Built-in concurrency primitives
The Main Function and Blocking
Before introducing concurrency, let’s understand how a normal Go program runs.
package main
import "fmt"
func main() {
fmt.Println("Task 1 started")
fmt.Println("Task 1 finished")
}
This program runs line by line, blocking until each statement completes.
Introducing Goroutines (Conceptual)
A goroutine is a lightweight unit of execution managed by the Go runtime.
Think of goroutines as extremely lightweight threads. You can create thousands of them without heavy resource usage.
Goroutines allow functions to run concurrently.
Creating a Goroutine
To start a goroutine, prefix a function call with the keyword go.
go myFunction()
This tells Go to run the function concurrently.
Simple Goroutine Example
package main
import (
"fmt"
"time"
)
func printMessage() {
fmt.Println("Hello from goroutine")
}
func main() {
go printMessage()
time.Sleep(time.Second)
fmt.Println("Main function finished")
}
Here, the goroutine runs concurrently with the main function. The sleep ensures the program does not exit before the goroutine executes.
Why Sleep Is Not a Good Solution
Using time.Sleep to wait for goroutines is unreliable.
It depends on timing and can break under load.
Go provides better synchronization tools, which you will learn in upcoming lessons.
Concurrency in Real Applications
Concurrency is used everywhere in modern Go applications:
- Web servers handling multiple clients
- Background job processing
- Microservices communication
- Data pipelines
Common Beginner Mistakes
- Forgetting that goroutines run asynchronously
- Program exiting before goroutines complete
- Accessing shared data without synchronization
Best Practices
- Keep goroutines small and focused
- Avoid shared mutable state
- Use channels for communication
- Use synchronization primitives when required
Practice Exercises
Exercise 1
Create a goroutine that prints a message while the main function prints another message.
Exercise 2
Modify the program to start multiple goroutines.
What’s Next?
In the next lesson, you will learn about Goroutines in Depth and how to use them effectively in real-world applications.