LoginSignup
4
2

More than 5 years have passed since last update.

マルチスレッドのConditionについて

Posted at

はじめに

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を外してしまうことです。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2