The book, "The Go Programming Language" has this simple goroutine example:
func main() {
go spinner(100 * time.Millisecond)
const n = 45
fibN := fib(n) // slow
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner(delay time.Duration) {
for {
for _, r := range `-\|/` {
fmt.Printf("\r%c", r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
Attempt #1, with std.concurrency:
import std.concurrency : spawn;
import core.thread : Thread;
import std.stdio : writefln, writef, stdout;
import std.datetime : msecs, Duration;
void main() @safe {
(() @trusted { spawn(&spinner, 100.msecs); })();
const n = 45;
const fibN = fib(n); // slow
writefln!"\rFibonacci(%d) = %d"(n, fibN);
}
void spinner(Duration delay) @safe {
(() @trusted { Thread.getThis.isDaemon(true); })();
while (true) {
foreach (char c; `-\|/`) {
writef!"\r%c"(c);
(() @trusted { stdout.flush; })();
(() @trusted { Thread.sleep(delay); })();
}
}
}
int fib(int x) pure @safe @nogc {
if (x < 2)
return x;
return fib(x - 1) + fib(x - 2);
}
This version has two problems:
-
a race condition with
isDaemon
: ifmain()
ends beforeisDaemon(true)
is called, then the program never ends because the kill-non-daemon-threads module destructor is called while the new thread isn't a daemon thread. -
it crashes about 10% of the time on exit (in dmd, gdc, and ldc). valgrind on a gdc build complains about "Conditional jump or move depends on uninitialised value(s)" early on.
Attempt #2, with std.parallelism:
import std.parallelism : task, taskPool;
import core.thread : Thread;
import std.stdio : writefln, writef, stdout;
import std.datetime : msecs, Duration;
void main() @safe {
auto spin = task!spinner(100.msecs);
taskPool.put(spin);
const n = 45;
const fibN = fib(n); // slow
writefln!"\rFibonacci(%d) = %d"(n, fibN);
}
void spinner(Duration delay) @safe {
while (true) {
foreach (char c; `-\|/`) {
writef!"\r%c"(c);
(() @trusted { stdout.flush; })();
(() @trusted { Thread.sleep(delay); })();
}
}
}
int fib(int x) pure @safe @nogc {
if (x < 2)
return x;
return fib(x - 1) + fib(x - 2);
}
This version continues to spin after the Fibonacci result is printed, despite https://dlang.org/phobos/std_parallelism.html#.taskPool saying that taskPool
worker threads are daemon by default, and despite various attempts to add isDaemon(true)
calls.
Is there a d version without these problems, and without varying substantially from the go (by e.g. having the spinner poll to see if it should exit gracefully).