はじめに
core.sync.condition.Condition
「え?ただのConditionだよ?知ってるよね?」みたいに書かれてる。
つらい。
Conditionとは
Java由来?のマルチスレッドの同期システムです。Javaとか滅せ。
コンストラクタの引数にしたMutexを使って、ロックの解除を待ったり、通知を待ったりします。
Conditionでwaitした場合、Mutexがロックされていなくて、かつConditionに通知(notify)されるまでスレッドを停止します。
例(1) ロック中にwaitした場合
Mutexをロックしているときにwaitすると、そのMutexのロックを解除して、待機状態に入ります。別スレッドがMutexをロックすると、notifyされても、Mutexがロック解除されるまでは待機を維持します。
import std.stdio, std.parallelism, core.thread, core.sync.condition, core.sync.mutex;
void main()
{
auto cond = new Condition(new Mutex);
// 000 100 200 300 400 500 600 [ms]
// 1: 1xxxxxxxxxxxxxooooooo-------6xxxxxx7 8!
// 2: 2------xxxxxx3!4xxxx5oooooooooooooo9
// 【凡例】
// x : ロック中
// - : 他のロック解除待ち
// o : 他からの通知待ち
// ! : 通知
// 000 100 200 300 400 500 600 [ms]
// 1: 1xxxxxxxxxxxxxooooooo-------6xxxxxx7 8!
taskPool.put(task(
{
Thread.sleep(0.msecs);
writeln(1);
cond.mutex.lock();
Thread.sleep(200.msecs);
// ここで一度ロックが外れる
// →3での通知を受けて再度ロックを試みて解除を待つ
// →5でロック解除されて制御が戻る
cond.wait();
writeln(6);
Thread.sleep(100.msecs);
writeln(7);
cond.mutex.unlock();
Thread.sleep(100.msecs);
writeln(8);
cond.notify();
}));
// 000 100 200 300 400 500 600 [ms]
// 2: 2------xxxxxx3!4xxxx5oooooooooooooo9
taskPool.put(task(
{
Thread.sleep(100.msecs);
writeln(2);
cond.mutex.lock();
Thread.sleep(100.msecs);
writeln(3);
cond.notify();
writeln(4);
Thread.sleep(100.msecs);
writeln(5);
// ここで一度ロックが外れる
// →8での通知を受けて再度ロックを試みてロック成功
cond.wait();
writeln(9);
cond.mutex.unlock();
}));
taskPool.finish();
}
例(2) ロック解除中にwaitした場合
ロック解除中にwaitした場合、ほかのスレッドからnotifyされるまで待機します。
import std.stdio, std.parallelism, core.thread,
core.sync.condition, core.sync.mutex;
void main()
{
auto cond = new Condition(new Mutex);
// 000 100 200 300 400 500 600 [ms]
// 1: 1ooooooooooooo-------6xxxxxx7 8!
// 2: 2xxxxx3!4xxxx5oooooooooooooox9
// 【凡例】
// x : ロック中
// - : 他のロック解除待ち
// o : 他からの通知待ち
// ! : 通知
// 000 100 200 300 400 500 600 [ms]
// 1: 1ooooooooooooo-------6xxxxxx7 8!
taskPool.put(task(
{
Thread.sleep(0.msecs);
writeln(1);
// 通知待ち
// →3での通知を受けてロックを試みて解除を待つ
// →5でロック解除されて制御が戻る
cond.wait();
writeln(6);
Thread.sleep(100.msecs);
writeln(7);
cond.mutex.unlock();
Thread.sleep(100.msecs);
writeln(8);
cond.notify();
}));
// 000 100 200 300 400 500 600 [ms]
// 2: 2xxxxx3!4xxxx5oooooooooooooox9
taskPool.put(task(
{
Thread.sleep(100.msecs);
writeln(2);
cond.mutex.lock();
Thread.sleep(100.msecs);
writeln(3);
cond.notify();
writeln(4);
Thread.sleep(100.msecs);
writeln(5);
// ここで一度ロックが外れる
// →8での通知を受けて再度ロックを試みてロック成功
cond.wait();
writeln(9);
cond.mutex.unlock();
}));
taskPool.finish();
}
D言語特有の問題
Conditionは性質上、当たり前のようにスレッド間で共有される資源です。つまり、sharedストレージクラスにしたくなる時があります。
しかしながら、Condition(をはじめとするcore.sync.*のモジュール)はsharedなメンバ関数を提供していません。
今我々にできる最良の手順は、castによってsharedを外してしまうことです。