Chapter 20: Threads
Level 3 -- Experience
Shared-memory concurrency with C threads, mutexes, and condition variables -- and why Auto's actor model makes most of this unnecessary.
Concurrency is where C programs most often go wrong. Data races, deadlocks, and subtle memory ordering bugs are notoriously difficult to reproduce and fix. C11 introduced a standard threading API based on POSIX threads. Auto takes a fundamentally different approach: actors with isolated state communicating through message passing.
20.1 Simple Interthread Control
C11 provides thrd_create and thrd_join for thread management:
// C Deep Dive: thread creation
#include <threads.h>
#include <stdio.h>
int worker(void *arg) {
int id = *(int *)arg;
printf("Worker %d started\n", id);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
printf("Worker %d finished, sum = %d\n", id, sum);
return 0;
}
int main(void) {
thrd_t t1, t2;
int id1 = 1, id2 = 2;
thrd_create(&t1, worker, &id1);
thrd_create(&t2, worker, &id2);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
return 0;
}C Deep Dive: C threads share the entire address space. Every thread can read and write every global variable. This makes data races trivially easy to create. The thread function must return
int(notvoid*like POSIX). Thethrd_createargument is passed asvoid*-- common source of type errors.
Auto's approach. Auto uses the actor model instead of raw threads:
// Auto: sequential execution (actor model is planned)
fn worker(id int) {
print("Worker", id, "started")
var sum int = 0
for i in 0..100 {
sum = sum + i
}
print("Worker", id, "finished, sum =", sum)
}
fn main() {
worker(1)
worker(2)
}