🎓 A1Learner
🔍
0 of 46 lessons completed

Zig Thread Basics

A thread is a separate path of execution inside one program. One program can run several threads at the same time.

One spawned thread

A worker writes 42 into a shared value. The main thread waits for the worker, then prints the value.

const std = @import("std");
fn setValue(value: *i32) void {
    value.* = 42;
}
pub fn main() !void {
    var out = std.io.getStdOut().writer();
    var value: i32 = 0;
    var t = try std.Thread.spawn(.{}, setValue, .{&value});
    t.join();
    try out.print("value={d}\n", .{value});
}

Two threads, two slots

Each worker writes to its own slot in an array. The main thread waits for both workers, then prints both slots in order.

const std = @import("std");
fn setSlot(slot: *i32, v: i32) void {
    slot.* = v;
}
pub fn main() !void {
    var out = std.io.getStdOut().writer();
    var results: [2]i32 = .{ 0, 0 };
    var t1 = try std.Thread.spawn(.{}, setSlot, .{ &results[0], 10 });
    var t2 = try std.Thread.spawn(.{}, setSlot, .{ &results[1], 20 });
    t1.join();
    t2.join();
    try out.print("slots={d} {d}\n", .{ results[0], results[1] });
}

A worker with an input

Spawn passes arguments to the worker function. The worker doubles its input and stores the result for the main thread to print.

const std = @import("std");
fn double(slot: *i32, n: i32) void {
    slot.* = n * 2;
}
pub fn main() !void {
    var out = std.io.getStdOut().writer();
    var result: i32 = 0;
    var t = try std.Thread.spawn(.{}, double, .{ &result, 21 });
    t.join();
    try out.print("doubled={d}\n", .{result});
}

Example

const std = @import("std");
fn square(slot: *i32, n: i32) void {
    slot.* = n * n;
}
pub fn main() !void {
    var out = std.io.getStdOut().writer();
    var results: [3]i32 = .{ 0, 0, 0 };
    var t1 = try std.Thread.spawn(.{}, square, .{ &results[0], 1 });
    var t2 = try std.Thread.spawn(.{}, square, .{ &results[1], 2 });
    var t3 = try std.Thread.spawn(.{}, square, .{ &results[2], 3 });
    t1.join();
    t2.join();
    t3.join();
    var total: i32 = 0;
    for (results) |v| {
        total += v;
    }
    try out.print("squares={d} {d} {d} total={d}\n", .{ results[0], results[1], results[2], total });
}

Key points

  • Spawn: to spawn a thread is to start a new path of execution running a function.
  • Worker: a worker is a function that runs on a spawned thread.
  • Handle: a handle is a value that refers to a running thread.
  • std.Thread.spawn returns a handle for the new thread.
  • The main thread waits for a spawned thread to finish before it reads any value that thread wrote.
  • Printing happens only in the main thread, after every spawned thread has finished.

Three workers each square one input into their own slot. The main thread waits for all three workers to finish. Only then does it print the slots and their total.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does std.Thread.spawn do?