Perl守护进程守护进程
我必须在我的代码中使用守护进程。 我需要一个控制守护进程,不断地检查数据库的任务,并监督子守护进程。 控制守护进程必须将任务分配给子守护进程,控制任务,如果其中一个死亡,则创build新的subprocess等等。子守护进程会检查数据库中的任务(通过PID)。 我应该如何实现这个目的的守护进程?
守护进程只是“运行很长时间的后台进程”的代码字。 所以答案是“这取决于”。 Perl有两种主要的多处理方式:
穿线
你运行一个子程序作为一个线程,与主程序代码并行。 (这可能只是监视线程状态)。
创build线程的开销较高,但更适合于“共享内存”风格的多处理,例如,当你传递大量数据时。 有几个库直接在线程间传递信息。 我个人很喜欢Thread::Queue
, Thread::Semaphore
和Storable
。
尤其是 – Storable
freeze
和thaw
让您可以在队列中移动复杂的数据结构(例如对象/哈希),这非常有用。
基本的线程示例:
#!/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'. Eg #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"; }
可收纳
当谈到可存储时,我认为这是一个单独的例子,因为移动数据很方便。
use Storable qw ( freeze thaw ); use MyObject; #home made object. use Thread::Queue; my $work_q = Thread::Queue->new(); sub worker_thread { while ( my $packed_item = $work_q->dequeue ) { my $object = thaw($packed_item); $object->run_some_methods(); $object->set_status("processed"); #maybe return $object via 'freeze' and a queue? } } my $thr = threads->create( \&worker_thread ); my $newobject = MyObject->new("some_parameters"); $work_q->enqueue( freeze($newobject) ); $work_q->end(); $thr->join();
因为你要在队列中传递对象,所以你要在线程间克隆它。 因此,请记住,您可能需要冻结它,并在完成某些内部状态后以某种方式“返回”。 但是这意味着你可以在不需要仲裁locking或共享内存的情况下asynchronous执行此操作。 你也可能发现能够“存储”和“检索”和对象是有用的 – 这可以像你期望的那样工作。 (虽然我敢说,如果你正在检索一个存储的对象,你可能需要小心模块版本的可用性与定义的属性)
分叉
你的脚本克隆自己,留下一个“父母”和“孩子” – 孩子然后一般分歧,做一些不同的事情。 这使用fork()
的Unix,结果是经过了很好的优化,通常非常高效 – 但是因为它的水平低,意味着很难做大量的数据传输。 进程间通信 – IPC会导致一些稍微复杂的事情。 (更多细节见perlipc
)。 它的效率并不是很低,因为大多数fork()
实现都会执行一个懒惰的数据拷贝 – 只有在需要的时候才会分配进程的内存空间。
因此,如果要委托许多不需要父母监督的任务,那就非常好。 例如,您可能会fork
Web服务器,因为孩子正在读取文件并将其传送给特定的客户端,而父母并不在乎。 或者,如果你想花费大量的CPU时间来计算结果,那么你可能会这样做,只能通过这个结果。
它在Windows上也不受支持。
有用的库包括Parallel::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();
哪个适合你?
所以很难说没有关于你想要完成的更多细节。 这就是为什么StacKOverflow通常喜欢显示一些工作,你试过的方法等。
我一般会说:
-
如果你需要传递数据,使用线程。
Thread::Queue
特别与Storable
结合使用时非常Storable
。 -
如果你不这样做,fork(在Unix上)通常更快/更高效。 (但是单靠快速通常是不够的 – 首先写可理解的东西,然后以速度为目标,通常情况并不重要)。
避免在可能的地方产生太多的线程 – 它们在内存和创build开销上相当密集。 在“工作者线程”编程风格中使用固定编号要比反复创build新的短暂线程要好得多。 (另一方面 – 叉子实际上是非常擅长的,因为他们不会复制你的整个过程)。
我会build议在你给的场景 – 你正在看线程和队列。 您的父进程可以通过threads -> list()
跟踪子线程threads -> list()
并join
或create
以保持正确的编号。 并可以通过中央队列将数据提供给工作线程。 或者有多个队列 – 每个“孩子”一个,并将其用作任务分配系统。