背景:
スレッドのサポートはPerl 5.005で登場しましたが、言語の実装の特性により、長い間実験的であり、多くのバグや制限を伴っていました。Perl 5.8以降、threads(およびthreads::shared)モジュールは、重大なタスクに対して十分に安定しましたが、Perlのスレッドモデルは多くの他のプログラミング言語とは大きく異なります。各スレッドはすべての変数のコピーを受け取り、明示的に宣言された構造体のみがthreads::sharedを介して共有アクセス可能です。
問題:
「通常の」変数はcopy-on-writeセマンティクスのためにスレッド間で見えません。threads::sharedなしでデータを分配しようとすると、状態が非同期になります。ロックが不適切に使用されると、レースコンディション、デッドロック、または不整合な変更の危険があります。
解決策:
共有変数を使用するには、use threads::sharedを介して宣言します。複数のスレッドが同時に読み書きする場合、lockを使用して共有データへのアクセスをブロックします。スレッドのライフサイクルを管理するために、join/detachメソッドを使用します。複雑な構造体の場合、sharedとして各要素を個別に宣言してください。そうしないと、「最上位」だけでは完全なスレッドセーフは保証されません。
コードの例:
use threads; use threads::shared; my $counter :shared = 0; my @threads; for (1..10) { push @threads, threads->create(sub { for (1..1000) { lock($counter); ++$counter; } }); } $_->join() for @threads; print "Counter: $counter ";
主な特徴:
:sharedは追加のロックなしでスレッドセーフを提供しますか?
いいえ。:shared属性は変数へのスレッド間のアクセスを提供しますが、変更(例えば、++や--)は原子的ではありません。各クリティカルセクションにはロックが必要です。
複雑な構造(ハッシュ配列など)を1つの:sharedディレクティブでスレッド間で共有できますか?
いいえ。配列またはハッシュの「上位レベル」だけが共有されます。各ネストされた要素もsharedにする必要があります。さもなければ、内部構造は他のスレッドに見えません。
スレッドがexitを呼び出すことで他のスレッドを終了させることはできますか?
いいえ。exitはプロセス全体を終了し、個別のスレッドを終了させません。スレッドを停止するには、スレッド内でexitを使用するか、join/detachのロジックで管理します。
2つのスレッドが同時にロックなしで$counter :sharedを増加させます。最終結果は期待したよりも少なくなります(一般的な失われた更新問題)。
利点:
欠点:
分離された変数の各変更に対してロックを実装します。大きな構造の場合は、ネストされたロックを要素ごとに実施します。
利点:
欠点: