Concurrency
37.1 Thread-Local by Default
Globals and statics are thread-local. Sharing requires shared, immutable, or __gshared. This prevents accidental data races.
37.2 Message Passing (std.concurrency)
Only immutable, shared, or value types (without mutable indirections) can be sent. This is enforced by the compiler.
spawnLinked, register/locate (named threads), setMaxMailboxSize, prioritySend.
import std.concurrency, std.stdio;
void worker()
{
bool running = true;
while (running)
{
receive(
(int n) { ownerTid.send(n * n); },
(string cmd) { if (cmd == "stop") running = false; },
(OwnerTerminated e) { running = false; }
);
}
}
void main()
{
auto tid = spawn(&worker);
foreach (i; 1 .. 4)
{
tid.send(i);
writeln(receiveOnly!int);
}
tid.send("stop");
auto got = receiveTimeout(100.msecs, (int x) {}); // with a timeout
}37.3 Threads (core.thread)
import core.thread;
auto t = new Thread({ writeln("in thread"); });
t.start();
t.join();
Thread.sleep(dur!"msecs"(100));
auto group = new ThreadGroup;
foreach (i; 0 .. 4) group.create({ /* work */ });
group.joinAll();37.4 shared, Synchronization and Atomics
Casting away shared inside a lock is the common (@trusted) pattern. -preview=nosharedaccess forbids direct access to shared data without atomics or casts.
import core.atomic, core.sync.mutex, core.sync.condition;
shared int counter;
void incr() { atomicOp!"+="(counter, 1); }
int v = atomicLoad(counter);
atomicStore(counter, 0);
cas(&counter, 0, 1); // compare-and-swap
class SafeCounter
{
private int count;
synchronized void increment() { count++; } // a synchronized class/method locks the monitor
synchronized int get() { return count; }
}
__gshared Mutex mtx;
shared static this() { mtx = new Mutex; }
void critical() { mtx.lock(); scope(exit) mtx.unlock(); /* ... */ }
synchronized (mtx) { /* block-level lock */ }
// Also: ReadWriteMutex, Condition, Semaphore, Barrier, core.sync.event