Concurrency and Parallelism
33.1 Threads (std/typedthreads)
Global variables accessed from threads require {.gcsafe.} reasoning. Use {.threadvar.} for thread-local storage.
import std/[typedthreads, locks] # Nim 2: threads are on by default (--threads:on)
var
counter = 0
lock: Lock
initLock(lock)
proc worker(n: int) {.thread.} =
for i in 1..n:
withLock lock:
inc counter
var ths: array[4, Thread[int]]
for t in ths.mitems: createThread(t, worker, 1000)
joinThreads(ths)
echo counter # 4000
deinitLock(lock)33.2 Channels
The legacy built-in Channel[T] (system module) still exists, but threading/channels is recommended under ORC.
import threading/channels # `threading` package (official)
var ch = newChan[string]()
proc producer(c: Chan[string]) {.thread.} =
for i in 1..3: c.send("msg " & $i)
var t: Thread[Chan[string]]
createThread(t, producer, ch)
for _ in 1..3: echo ch.recv()
joinThread(t)33.3 Atomics and Synchronization
std/atomics (Atomic[int], fetchAdd, load, store, memory orders), std/locks (Lock, Cond), std/rlocks, threading/smartptrs (SharedPtr, UniquePtr, ConstPtr), threading/atomics, threading/barrier, threading/waitgroups.
33.4 Task Parallelism Libraries
malebolgia (Araq's structured concurrency, recommended) m.spawn within an awaitAll block taskpools (Status)
Thread pool with spawn/sync and FlowVars High-performance work stealing (data and task parallelism)
import malebolgia
proc fib(n: int): int = (if n < 2: n else: fib(n-1) + fib(n-2))
var results: array[4, int]
var m = createMaster()
m.awaitAll:
for i in 0..3:
m.spawn fib(30 + i) -> results[i]
echo results33.5 Parallel Iteration
Weave's parallelFor, malebolgia's parApply, or splitting work manually into chunks.
Data races: prefer message passing and isolation. Use --threadAnalysis and run ThreadSanitizer (--passC:-fsanitize=thread).