【Perl】threadsモジュールを使ってみる

仕事で書いたPerlスクリプトのスピードアップのためにスレッド導入を検討しています。こちらは現在の作業環境(Perl v5.32.1)では標準で入っているthreadsモジュールの実験メモです。

use threads;

threadsモジュールが動く実験してみる。

use threads;

my $worker = threads->create(\&heavy_process);

exit;

sub heavy_process {
  print "start heavy process.\n";
  
  my $times = 5;
  
  foreach my $i (1 .. $times) {
    sleep 1;
    print "[$i / $times]\n";
  }
  
  print "end heavy process.\n";
}

createメソッドにスレッドに実行してほしいサブルーチン(ここではheavy_process)のリファレンスを渡します。コマンドプロンプトで実行してみると、動いたけどなんか思ったのと違う。

start heavy process.
        1 running and unjoined
        0 finished and unjoined
        0 running and detached

スレッドが終わるまで待ってあげる必要があるようでした。createメソッドを書いたあとにjoinメソッドを書くとスレッドが終わるまで待機してくれる。

my $worker = threads->create(\&heavy_process);

$worker->join();

exit;

ちゃんと思ったように動いた。

start heavy process.
[1 / 5]
[2 / 5]
[3 / 5]
[4 / 5]
[5 / 5]
end heavy process.

スレッドを複数にしてみる

さっきはスレッド1つだったけど、複数で動かせるか試してみる。一気に4つまで増やしてみる。誰が仕事しているか判別できるように名前を表示するようにしてみた。引数を渡すときはcreateメソッドの第2引数移行に渡したい値を書けばよい。

use threads;

my $worker_first  = threads->create(\&heavy_process, 'Ivan');
my $worker_second = threads->create(\&heavy_process, 'Jet');
my $worker_third  = threads->create(\&heavy_process, 'Francoise');
my $worker_force  = threads->create(\&heavy_process, 'Albert');

$worker_first->join();
$worker_second->join();
$worker_third->join();
$worker_force->join();

exit;

sub heavy_process {
  my $name = shift;
  
  print "{$name} start heavy process.\n";
  
  my $times = 5;
  
  foreach my $i (1 .. $times) {
    sleep 1;
    print "{$name} [$i / $times]\n";
  }
  
  print "{$name} end heavy process.\n";
}

スレッドごとに処理が実行されている。最初に仕事を始めたIvanが最初に終了するわけではないようです。下記の結果はスクリプトを実行するたびに変化しました。

{Ivan} start heavy process.
{Jet} start heavy process.
{Francoise} start heavy process.
{Albert} start heavy process.
{Francoise} [1 / 5]
{Jet} [1 / 5]
{Ivan} [1 / 5]
{Albert} [1 / 5]
{Jet} [2 / 5]
{Francoise} [2 / 5]
{Ivan} [2 / 5]
{Albert} [2 / 5]
{Francoise} [3 / 5]
{Jet} [3 / 5]
{Ivan} [3 / 5]
{Albert} [3 / 5]
{Jet} [4 / 5]
{Francoise} [4 / 5]
{Ivan} [4 / 5]
{Albert} [4 / 5]
{Francoise} [5 / 5]
{Francoise} end heavy process.
{Jet} [5 / 5]
{Jet} end heavy process.
{Ivan} [5 / 5]
{Ivan} end heavy process.
{Albert} [5 / 5]
{Albert} end heavy process.

返り値をもらう

スレッドに処理してもらうサブルーチンから返り値をもらう場合はjoinメソッドが返してくれるので、さらに実験してみる。さきほどはスレッドたちは何かを5回処理していたけど、ランダムで1~5回処理してもらって、何回やったか教えてもらう。

use threads;

my $worker_first  = threads->create(\&heavy_process, 'Ivan');
my $worker_second = threads->create(\&heavy_process, 'Jet');
my $worker_third  = threads->create(\&heavy_process, 'Francoise');
my $worker_force  = threads->create(\&heavy_process, 'Albert');

my $processed_first  = $worker_first->join();
my $processed_second = $worker_second->join();
my $processed_third  = $worker_third->join();
my $processed_force  = $worker_force->join();

print "\n";
print "$processed_first\n";
print "$processed_second\n";
print "$processed_third\n";
print "$processed_force\n";

exit;

sub heavy_process {
  my $name = shift;
  
  print "{$name} start heavy process.\n";
  
  my $times = int(rand(5)) + 1;
  
  foreach my $i (1 .. $times) {
    sleep 1;
    print "{$name} [$i / $times]\n";
  }
  
  print "{$name} end heavy process.\n";
  
  return "{$name} processed $times times.";
}

返り値をもらうことも出来ました。下記の結果もスクリプトを実行するたびに変化、今回はJetが仕事をサボったようです。

{Ivan} start heavy process.
{Jet} start heavy process.
{Francoise} start heavy process.
{Albert} start heavy process.
{Ivan} [1 / 4]
{Albert} [1 / 2]
{Jet} [1 / 1]
{Francoise} [1 / 3]
{Jet} end heavy process.
{Ivan} [2 / 4]
{Albert} [2 / 2]
{Albert} end heavy process.
{Francoise} [2 / 3]
{Ivan} [3 / 4]
{Francoise} [3 / 3]
{Francoise} end heavy process.
{Ivan} [4 / 4]
{Ivan} end heavy process.

{Ivan} processed 4 times.
{Jet} processed 1 times.
{Francoise} processed 3 times.
{Albert} processed 2 times.

参考文献

今回のthreadsのテストはperldocの資料を参考にしました。まだまだ試していないことがあるので、また実験してみよう。


過去の同じ日の記事一覧


Jess BaileyによるPixabayからの画像を使用しています。

お読みくださりありがとうございます。この記事が何かの参考になりましたら、ブログランキングサイトのバナークリックをよろしくお願いします。

ブログランキング・にほんブログ村へ 人気ブログランキング FC2 ブログランキング