lock

スレッドの共有変数をロックする

構文

解説

lock はスレッド環境で THING に与えた共有変数や参照オブジェクトをアドバイザリーロックします。 該当の変数がスコープを抜けるとロックが解除されます。 lock は、THING がスカラーならそのスカラーそのものを返します。 THING がハッシュ、配列、サブルーチンなら、その参照を返します。

lockuse threads::shared の環境下でのみ機能します。 そうでない場合は lock は何もしません。 詳細は threads::shared の解説をご覧ください。

lock を使うことで、複数のスレッドで共有する変数をスレッドセーフな変数にして、 競合状態を避けることができます。 一つのスレッドで共有変数が lock ロックされると、 そのロックが解除されるまでの間、他のスレッドでは同じ変数を lock でロックしようとしても待たされることになります。

次のサンプルコードは、2 つのスレッドを生成して同時に開始します。 2 つのスレッドは変数 $counter を共有しており、それぞれのスレッドで $counter をインクリメントする処理をループで 5 回行っています。 ループ内の処理では、インクリメントの直前に lock$counter をロックし、 インクリメント後に 1 秒間待ちます。ここでスコープを抜けます。 スコープを抜けた後に、さらに 1 秒待ち、ループを継続します。 2 つのスレッドの処理はほとんど同じですが、結果を見やすくするために、一方は 1 だけインクリメント、 もう一方は 10 だけインクリメントします。

use threads;
use threads::shared;

# スレッド間で共有する変数
my $counter : shared = 0;

# 1 つ目のスレッド処理
sub incr_counter1 {
    for ( my $i = 1 ; $i <= 5 ; $i++ ) {
        {
            # 変数をロック
            lock($counter);
            $counter += 1;
            print "Thread 1: ${counter}\n";
            sleep(1);

            # ここでロックが解除される
        }
        sleep(1);
    }
}

# 2 つ目のスレッド処理
sub incr_counter2 {
    for ( my $i = 1 ; $i <= 5 ; $i++ ) {
        {
            # 変数をロック
            lock($counter);
            $counter += 10;
            print "Thread 2: ${counter}\n";
            sleep(1);

            # ここでロックが解除される
        }
        sleep(1);
    }
}

# スレッドを生成
my $thread1 = threads->create( \&incr_counter1 );
my $thread2 = threads->create( \&incr_counter2 );

# スレッドを開始
$thread1->join();
$thread2->join();

上記サンプルコードを実行すると、次のような結果が出力されます。

Thread 1: 1
Thread 2: 11
Thread 1: 12
Thread 2: 22
Thread 1: 23
Thread 2: 33
Thread 1: 34
Thread 2: 44
Thread 1: 45
Thread 2: 55

まず 1 つ目のスレッドでは、lock によって $counter がロックされます。そして、そのスコープを抜けるまでに 1 秒かかります。 つまりこのロックが解除されるのに 1 秒かかることを意味します。 同時に 2 つ目のスレッドも実行が開始されていますが、1 つ目のスレッドによって $counter がロックされているため、2 つ目のスレッドの lock は待たされることになります。 これをお互いのスレッドで繰り返されることになります。