Concurrency Part 1- Understanding Concurrency in Go
Tue Feb 20 2024
Concurrency Fundamentals
Concurrency is the ability of a program to deal with multiple tasks in an overlapping manner. This doesn't necessarily mean they execute at the exact same instant (that's parallelism), but rather that progress can be made on one task while momentarily waiting on another.
Threads: The Traditional Approach
Languages like Python and Java traditionally rely on operating system threads for concurrency. Each thread encapsulates its own execution flow and has its own dedicated stack. While powerful, key challenges include:
- Overhead: Threads have significant memory overhead and context switching between them can be relatively slow.
- Synchronization Complexity: Shared data needs careful synchronization (using locks, etc.) to prevent race conditions.
Go's Approach: Goroutines and Channels
Go takes a radically different approach:
- Goroutines: Extremely lightweight "threads" managed by the Go runtime. They have tiny initial stack sizes that grow as needed, and the runtime efficiently schedules them across available threads.
- Channels: Typed conduits for communication between goroutines. Sending and receiving data on channels provides implicit synchronization.
Example: Simulating Network Requests
Let's see a simplified example to contrast approaches:
Python (threading):
import threading
import time
def fetch_data(url):
# ... simulate network call ...
time.sleep(1)
print(f"Fetched data from: {url}")
threads = []
for url in ['https://site1', 'https://site2']:
t = threading.Thread(target=fetch_data, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
Go (channels):
The snippet below demonstrates how to fetch data from multiple websites simultaneously in Go. It uses goroutines to make network requests concurrently and a channel to collect and synchronize the results.
package main
import (
"fmt"
"net/http"
"time"
)
func fetch_data(url string, ch chan string) {
// Simulate an actual network request
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("Error fetching from %s: %v", url, err)
return
}
// Simulate some processing time
time.Sleep(2 * time.Second)
ch <- fmt.Sprintf("Fetched %d bytes from: %s", resp.ContentLength, url)
}
func main() {
ch := make(chan string)
for _, url := range []string{"[https://www.google.com](https://www.google.com)", "[https://www.example.com](https://www.example.com)"} {
go fetch_data(url, ch)
}
for i := 0; i < 2; i++ {
fmt.Println(<-ch)
}
}
I hope this post has provided you with some useful insights!
Happy coding in Go!