Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The async module

Threads and concurrency, modeled on Rust’s std::thread and C++ std::thread, and tasks: green threads that take turns on a few worker threads.

Like io, async is a module: bring it in with import async (and write async.Thread, async.logical_cpus()) or import names directly with import [Thread, ThreadLocal] from async.

  • Atomic: Atomic<T>, a value threads read and update without a lock
  • Thread: starting, naming and joining threads, ThreadId and ThreadBody
  • ThreadLocal: a variable with a separate value on every thread, and its ThreadLocalGuard<T>
  • Processor counts: logical_cpus() and physical_cpus()
  • Channel: Channel<T>, a queue that carries values between threads, and its ChannelIterator<T>
  • Mutex: Mutex<T>, the MutexGuard<T> a with block holds, and Condition<T>
  • Once: Once, which runs a function once, and Lazy<T>, a value built on first use
  • Tasks: run, Task<T>, spawn, spawn_on_each_worker, run_blocking, sleep, yield_now, timeout and cancellation, for many tasks taking turns on a few worker threads and parking on sockets
  • TaskLocal: a variable with a separate value in every task, and its TaskLocalGuard<T>
  • I/O backends: Backend, how a runtime waits for sockets

Only thread-safe values can be shared between threads: numbers, shared function values (such as a thread’s body), and [Shared] structs such as Atomic<T>, Thread, ThreadLocal<T>, Channel<T> and Mutex<T>. Any other value crosses threads only as a new value that nothing else references (Channel.send(move(x)), or send(copy(x))), or stays inside a Mutex, whose with blocks the compiler keeps apart from the rest of the program. Lazy<T> holds thread-safe values only. See the language guide’s Threads chapter.

Example

import async;

const hits = async.Atomic<long>(0);

work() {
    hits.fetch_add(1);
}

main(): int {
    var workers = Array<async.Thread>();
    var n = async.logical_cpus();
    var i: ulong = 0;
    while (i < n) {
        workers.append(async.Thread.spawn(work));
        i = i + 1;
    }
    for (var worker in workers) {
        worker.join();
    }
    assert(hits.load() == n as long, "every worker ran");
    return 0;
}

Source: runtime/async/module.pep