4

背景:
私の perl スクリプトをマルチスレッド化する方法を読む際に、( http://perldoc.perl.org/threads.html#BUGS-AND-LIMITATIONSから)を読みました

ほとんどのシステムでは、スレッドの頻繁かつ継続的な作成と破棄により、Perl インタープリターのメモリ フットプリントがますます増大する可能性があります。スレッドを起動してから ->join() または ->detach() するのは簡単ですが、寿命の長いアプリケーションの場合は、スレッドのプールを維持し、キューを使用して必要な作業にそれらを再利用することをお勧めします。スレッドに保留中の作業を通知します。

私のスクリプトは長持ちします。これは、常に実行されている PKI LDAP ディレクトリ監視デーモンです。エンタープライズ監視ソリューションは、何らかの理由で実行を停止するとアラームを生成します。私のスクリプトは、別の PKI LDAP ディレクトリにアクセスできることを確認し、両方の失効リストを検証します。

問題: Google で見つけることができるすべてのものは、サブルーチン自体ではなく、変数 (スカラーなど) をスレッド キューに渡すことを示しています...スレッドの実装方法と比較して、スレッド キューを適切に実装する方法を理解していないと思います (キューなし)。

質問 1 : perl インタープリターがメモリを徐々に消費するのを避けるために、「スレッドのプールを維持する」にはどうすればよいですか?
質問2: (関係ありませんが、このコードを投稿している間) スレッドを 1 分間に 2 回以上開始しないように、メイン プログラムの最後に安全な量のスリープがありますか? 60 は当たり前のように思えますが、ループが高速な場合、または処理時間などのために 1 分も逃した場合に、ループが複数回実行される可能性はありますか?

前もって感謝します!

#!/usr/bin/perl

use feature ":5.10";
use warnings;
use strict;
use threads;
use Proc::Daemon;
#

### Global Variables
use constant false => 0;
use constant true  => 1;
my $app = $0;
my $continue = true;
$SIG{TERM} = sub { $continue = false };

# Directory Server Agent (DSA) info
my @ListOfDSAs = (
    { name => "Myself (inbound)",
      host => "ldap.myco.ca",
      base => "ou=mydir,o=myco,c=ca",
    },
    { name => "Company 2",
      host => "ldap.comp2.ca",
      base => "ou=their-dir,o=comp2,c=ca",
    }
);    
#

### Subroutines

sub checkConnections
{   # runs every 5 minutes
    my (@DSAs, $logfile) = @_;
    # Code to ldapsearch
    threads->detach();
}

sub validateRevocationLists
{   # runs every hour on minute xx:55
    my (@DSAs, $logfile) = @_;
    # Code to validate CRLs haven't expired, etc
    threads->detach();
}

#

### Main program
Proc::Daemon::Init;

while ($continue)
{
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

    # Question 1: Queues??

    if ($min % 5 == 0 || $min == 0)
        { threads->create(&checkConnections, @ListOfDSAs, "/var/connect.log"); }

    if ($min % 55 == 0)
        { threads->create(&validateRevocationLists, @ListOfDSAs, "/var/RLs.log"); }

    sleep 60; # Question 2: Safer/better way to prevent multiple threads being started for same check in one matching minute?
}

# TERM RECEIVED
exit 0;
__END__
4

2 に答える 2

1

ここでスレッドを使用する説得力のある理由はないようで、常に実行されるプログラムに不必要な複雑さを加えたくないため、一度に 1 つずつチェックを実行することをお勧めします。

スレッド プールの使用方法を知りたい場合は、モジュールにサンプルが含まれていthreadsます。便利なThread::Poolモジュールもあります。

同じ分にチェックを繰り返さないようにすることについてはsleeping、60 秒では不十分であることは間違いありません。スリープにどの値を選択しても、失敗するエッジ ケースがあります。1 分よりわずかに短く、同じ 1 分間に 2 つのチェックが発生する場合があるか、1 分よりわずかに長くなる場合があります。 、チェックをまったく見逃すこともあります。

代わりに、変数を使用して、タスクが最後にいつ完了したかを覚えておいてください。これにより、1 分間に複数のチェックを気にすることなく、より短いスリープ時間を使用できます。

my $last_task_time = -1;
while ($continue)
{
    my $min = (localtime(time))[1];

    if ($last_task_time != $min && 
          ($min % 5 == 0 || $min > ($last_task_time+5)%60))
    { 
        #Check connections here.

        if ($min == 55 || ($last_task_time < 55 && $min > 55))
        { 
           #Validate revocation lists here.
        }

        $last_task_time = $min;
    }
    else
    {
        sleep 55; #Ensures there is at least one check per minute.
    }
}

更新:最後のタスクが長時間実行された場合に回復するようにコードを修正しました。たまに長い時間がかかる場合は、これで問題ありません。ただし、タスクに 5 分以上かかることが頻繁にある場合は、別の解決策が必要です (その場合、スレッドはおそらく理にかなっています)。

于 2013-04-25T14:31:00.730 に答える