Using synchronized to Avoid Race Conditions
Learn how synchronized can be used to avoid race conditions.
We'll cover the following...
Use of synchronized
The incorrect program behavior in the previous lesson is due to more than one thread accessing the same mutable data (and at least one of them modifying it). One way of avoiding these race conditions is to mark the common code with the synchronized
keyword. The program will work correctly with the following change:
import std.stdio;import std.concurrency;import core.thread;void swapper(shared(int) * a, shared(int) * b) {foreach (i; 0 .. 10_000) {synchronized {int temp = *b;*b = *a;*a = temp;}}}void main() {shared(int) i = 1;shared(int) j = 2;writefln("before: %s and %s", i, j);foreach (id; 0 .. 10) {spawn(&swapper, &i, &j);}// Wait for all threads to finish their tasksthread_joinAll();writefln("after : %s and %s", i, j);}
The output:
before: 1 and 2
after : 1 and 2 ← correct result
The effect of synchronized
is to create a lock behind the scenes and allow only one thread to hold that lock at a given time. Only the thread that holds the lock can be executed, and the others wait until the lock becomes available again when the executing thread completes its synchronized block. Since one thread executes the synchronized code at a time, each thread would now swap ...