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