Concurrency
Running multiple tasks concurrently
Introduction
In this lesson, you'll learn about concurrency in Go. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In C, you're familiar with running multiple tasks concurrently.
Go has its own approach to running multiple tasks concurrently, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
package main
import (
"fmt"
"sync"
)
func worker(id int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
results <- id * 2
}
func main() {
results := make(chan int, 5)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i, results, &wg)
}
// Close channel when all workers done
go func() {
wg.Wait()
close(results)
}()
for r := range results {
fmt.Println("result:", r)
}
}Comparing to C
Here's how you might have written similar code in C:
#include <pthread.h>
#include <stdio.h>
typedef struct {
int id;
int result;
} WorkerArgs;
void *worker(void *arg) {
WorkerArgs *w = (WorkerArgs *)arg;
w->result = w->id * 2;
return NULL;
}
int main() {
pthread_t threads[5];
WorkerArgs args[5];
for (int i = 0; i < 5; i++) {
args[i].id = i;
pthread_create(&threads[i], NULL, worker, &args[i]);
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
printf("result[%d] = %d\n", i, args[i].result);
}
}You may be used to different syntax or behavior.
Go goroutines are much lighter than pthreads (KB vs MB stack)
You may be used to different syntax or behavior.
Go channels provide safe communication; C needs mutexes for shared memory
You may be used to different syntax or behavior.
Go's go keyword is simpler than pthread_create
You may be used to different syntax or behavior.
Go's select statement elegantly handles multiple channels; C has no equivalent
Step-by-Step Breakdown
1. Goroutines vs pthreads
A goroutine starts with just 'go func()'. Creating thousands of goroutines is normal; thousands of pthreads would exhaust OS resources.
pthread_create(&thread, NULL, worker, &args);go worker(id, results, &wg)2. Channels vs Shared Memory
Go's philosophy: 'Do not communicate by sharing memory; share memory by communicating.' Channels pass data safely between goroutines.
ch := make(chan int)
go func() { ch <- 42 }()
val := <-ch3. sync.WaitGroup vs pthread_join
WaitGroup tracks when all goroutines finish, equivalent to joining all threads but without needing thread IDs.
pthread_join(thread, NULL);wg.Add(1); go func() { defer wg.Done(); work() }()
wg.Wait()Common Mistakes
When coming from C, developers often make these mistakes:
- Go goroutines are much lighter than pthreads (KB vs MB stack)
- Go channels provide safe communication; C needs mutexes for shared memory
- Go's go keyword is simpler than pthread_create
Key Takeaways
- go keyword replaces pthread_create
- Channels replace shared memory + mutexes for communication
- sync.WaitGroup replaces pthread_join
- Goroutines are far lighter than OS threads