threadsに関する事。
間違ってても誰かが指摘してくれる。
joinの重要性
$thread = threads->new(\&func);
$thread->join;
スレッドが生成されるのはnew(create)をした直後で、同時に実行もされる。
joinはスレッドの終了待ちをするため、メインスレッドでの処理が止まる。
どっかにjoinでスレッドを実行するみたいなことを書いていたところがあったので。
そんなことはどうでもよくて、重要なところは、
メモリを開放するのはjoinを終えてからということ。
つまり、スレッドの処理自体は完全に終わっても、そのスレッドをjoinをしなければ、メモリが開放されない。
なので、joinしていないスレッドがたまると、メモリがすぐに使用率いっぱいになる。
排他制御によるスレッド生成制限
use threads; use Thread::Semaphore; my $thr_sem = new Thread::Semaphore(10); # 10個 main(); sub main{ foreach(1 .. 100) { $thr_sem->down; threads->new(\&func, $_); } foreach my $thr(threads->list){ $thr->join; } } sub func{ print "$_\n"; sleep 1; $thr_sem->up; }
スレッドを実行する順番は不定だが、常に10個になるようにスレッドを作って実行する。
Queueを使ったjoin制御
use threads; use threads::shared; use Thread::Semaphore; use Thread::Queue; my $thr_que = new Thread::Queue(); my $thr_sem = new Thread::Semaphore(10); # 10個 main(); sub main{ # 処理用スレッド my $call_thr = threads->new(sub{ foreach(1 .. 100) { $thr_sem->down; threads->new(\&func, $_); } }); # キュー監視用スレッド my $queue_thr = threads->new(\&thr_que_join); $call_thr->join(); # func処理を待つ $thr_que->enqueue(undef); # 番兵 $queue_thr->join(); # キューの始末を待つ # func処理に時間がかかるとundefより後にスレッドIDがくるので後始末処理。 sleep 1 while( scalar($thr_que->pending()) != scalar(threads->list()) ); $thr_que->enqueue(undef); # 番兵 thr_que_join(); # キューの掃除 } # 終わったスレッドを順次joinしていく。undefが来ると終了。 sub thr_que_join{ while( my $thr = $thr_que->dequeue()){ threads->object($thr)->join(); } } sub func{ my $no = shift; print "$no\n"; sleep 1; $thr_que->enqueue(threads->self->tid); # 完了したら自身のスレッドIDを登録。 $thr_sem->up; }
スレッド用の関数を実行するスレッドと、joinするためのスレッドを作り、
処理が終わったスレッドIDをキューにいれ、順次キューから取り出してjoinしていくというもの。
joinの事を考えなくて済むのと、処理が終わったものからjoinするのでメモリが溜まりにくい。
欠点はfunc時に予想外の終わり方をすると、
スレッドIDがキューに登録されなかったり、セマフォ操作によるスレッド数制限がおかしくなる。
致命的なエラーになりえる処理はevalでトラップするのもあり?
sleep 1 while・・・って辺りの3行は、もうわかんないから動けばいいと思って書いた。
$_->join foreach(sort {$a->tid <=> $b->tid } threads->list());
とでもして、スレッドIDが若い順に並べて、単純にjoin待ちすればいいような気もした。