带子守护进程的Perl守护进程

带子守护进程的Perl守护进程,perl,daemon,Perl,Daemon,我必须在代码中使用守护进程。我需要一个控制守护进程,它不断检查数据库中的任务,并监督子守护进程。控制守护进程必须将任务分配给子守护进程、控制任务、在其中一个死亡时创建新的子守护进程等。子守护进程检查数据库中是否有任务(通过PID)。为此,我应该如何实现守护进程?守护进程只是“运行时间长的后台进程”的一个代码词。所以答案是“视情况而定”。Perl有两种主要的多处理方法: 穿线 您可以将子例程作为线程与主程序代码并行运行。(然后可能只是监视线程状态) 创建线程的开销更高,但它更适合于“共享内存”式的

我必须在代码中使用守护进程。我需要一个控制守护进程,它不断检查数据库中的任务,并监督子守护进程。控制守护进程必须将任务分配给子守护进程、控制任务、在其中一个死亡时创建新的子守护进程等。子守护进程检查数据库中是否有任务(通过PID)。为此,我应该如何实现守护进程?

守护进程只是“运行时间长的后台进程”的一个代码词。所以答案是“视情况而定”。Perl有两种主要的多处理方法:

穿线 您可以将子例程作为线程与主程序代码并行运行。(然后可能只是监视线程状态)

创建线程的开销更高,但它更适合于“共享内存”式的多处理,例如在来回传递大量数据时。有几个库使线程之间的信息传递变得非常简单。就我个人而言,我非常喜欢,而且

特别是-
Storable
具有
冻结
解冻
功能,可以在队列中移动复杂的数据结构(例如对象/散列),这非常有用

基本线程示例:

#!/usr/bin/perl

use strict;
use warnings;

use threads;

use Thread::Queue;

my $nthreads = 5;

my $process_q = Thread::Queue->new();
my $failed_q  = Thread::Queue->new();

#this is a subroutine, but that runs 'as a thread'.
#when it starts, it inherits the program state 'as is'. E.g.
#the variable declarations above all apply - but changes to
#values within the program are 'thread local' unless the
#variable is defined as 'shared'.
#Behind the scenes - Thread::Queue are 'shared' arrays.

sub worker {
    #NB - this will sit a loop indefinitely, until you close the queue.
    #using $process_q -> end
    #we do this once we've queued all the things we want to process
    #and the sub completes and exits neatly.
    #however if you _don't_ end it, this will sit waiting forever.
    while ( my $server = $process_q->dequeue() ) {
        chomp($server);
        print threads->self()->tid() . ": pinging $server\n";
        my $result = `/bin/ping -c 1 $server`;
        if ($?) { $failed_q->enqueue($server) }
        print $result;
    }
}

#insert tasks into thread queue.
open( my $input_fh, "<", "server_list" ) or die $!;
$process_q->enqueue(<$input_fh>);
close($input_fh);

#we 'end' process_q  - when we do, no more items may be inserted,
#and 'dequeue' returns 'undefined' when the queue is emptied.
#this means our worker threads (in their 'while' loop) will then exit.
$process_q->end();

#start some threads
for ( 1 .. $nthreads ) {
    threads->create( \&worker );
}

#Wait for threads to all finish processing.
foreach my $thr ( threads->list() ) {
    $thr->join();
}

#collate results. ('synchronise' operation)
while ( my $server = $failed_q->dequeue_nb() ) {
    print "$server failed to ping\n";
}
因为您在队列中传递对象,所以实际上是在线程之间克隆对象。所以请记住,一旦你对它的内部状态做了一些事情,你可能需要冻结它并以某种方式“返回”它。但这确实意味着您可以异步执行此操作,而无需仲裁锁定或共享内存。您可能还会发现,能够“存储”和“检索”对象也很有用——这正如您所期望的那样有效。(尽管我敢说,如果要检索存储对象,您可能需要注意模块版本的可用性和定义的属性)

分叉 你的脚本会自我克隆,留下一个“父”和“子”——子脚本通常会发散并做一些不同的事情。这使用了Unix内置的
fork()
,因此得到了很好的优化,通常效率很高,但由于其级别较低,这意味着很难进行大量数据传输。您将完成一些稍微复杂的事情来进行进程间通信-IPC。(有关更多详细信息,请参见)。它的效率很高,这不仅是因为大多数
fork()
实现都会进行延迟数据复制—进程的内存空间仅在需要时分配,例如,当它发生更改时

因此,如果您想委派许多不需要家长过多监督的任务,这是非常好的。例如,您可能会
fork
web服务器,因为孩子正在读取文件并将其交付给特定的客户机,而家长并不太在意。或者,如果您想花费大量CPU时间计算一个结果,并且只返回该结果,您可能会这样做

Windows上也不支持它

有用的库包括

“分叉”代码的一个基本示例如下所示:

#!/usr/bin/perl
use strict;
use warnings;
use Parallel::ForkManager;

my $concurrent_fork_limit = 4;

my $fork_manager = Parallel::ForkManager->new($concurrent_fork_limit);

foreach my $thing ( "fork", "spoon", "knife", "plate" ) {
    my $pid = $fork_manager->start;
    if ($pid) {
        print "$$: Fork made a child with pid $pid\n";
    } else {
        print "$$: child process started, with a key of $thing ($pid)\n";
    }
    $fork_manager->finish;
}

$fork_manager->wait_all_children();
哪个适合你? 因此,很难说没有一点关于你想要完成什么的细节。这就是为什么StacKOverflow通常喜欢展示一些您尝试过的有效方法等

我一般会说:

  • 如果需要传递数据,请使用线程<代码>线程::队列特别是与可存储结合使用时,它非常适合

  • 如果不这样做,fork(在Unix上)通常更快/更高效。(但仅仅快速通常是不够的——首先写一些可以理解的东西,其次是追求速度。通常这并不重要)

尽可能避免产生太多线程-它们占用大量内存和创建开销。在“工作线程”风格的编程中使用固定数量要比重复创建新的短期线程好得多。(另一方面,fork实际上非常擅长于此,因为它们不会复制您的整个过程)


我建议在您给出的场景中—您正在查看线程和队列。您的父进程可以通过
threads->list()
join
create
跟踪子线程,以保持正确的编号。并且可以通过中心队列将数据提供给工作线程。或者有多个队列-每个“子”一个队列,并将其用作任务分配系统

我需要一个固定数量的工人恶魔(5)。他们应该从数据库中检出一些变量,然后从邮件服务中获取消息。我认为fork对我来说是一种更好的方式,恶魔只会处理消息(但附件可能很大)。ForkManager会让你有5个活动的“任务”——你可以在fork之前设置一个变量,ID是获取和处理什么。然后fork,进行处理,退出并加入该fork。然后ForkManager会将fork的数量保持在5,但它会为每条消息生成一个新的fork,这可能会起到作用。也许这个问题听起来很愚蠢,但子进程和主守护进程的实现必须在单独的文件中?如果在其中,一个孩子将如何理解他们的代码?父和子是一个特定的术语,表示一个在他们的代码中产生另一个。如果您有完全独立的脚本,那么您可能仍然希望查看perlipc链接,该链接讨论进程之间的通信方式。就我个人而言,我发现多线程是IPC最通用的形式之一。至于forking和
并行:ForkManager
,它有助于将数据传回。是的。正如你所说,线程当然更好。
#!/usr/bin/perl
use strict;
use warnings;
use Parallel::ForkManager;

my $concurrent_fork_limit = 4;

my $fork_manager = Parallel::ForkManager->new($concurrent_fork_limit);

foreach my $thing ( "fork", "spoon", "knife", "plate" ) {
    my $pid = $fork_manager->start;
    if ($pid) {
        print "$$: Fork made a child with pid $pid\n";
    } else {
        print "$$: child process started, with a key of $thing ($pid)\n";
    }
    $fork_manager->finish;
}

$fork_manager->wait_all_children();