perl多进程和进程池

来源:互联网 发布:js数值转换成字符串 编辑:程序博客网 时间:2024/04/20 03:15

故事:

        事情是这样的。突然接到一个任务,要求把HDFS上的数据copy一倍,用于压力测试。我仔细检查了HDFS的数据,发现每个数据目录下的文件数量在1000个左右。要求把每个文件copy一遍,重命名,放在相同的目录下。于是,我就写了个perl脚本,依次对每个文件夹下的每个文件复制一次。

        悲剧就这样发生了。脚本跑了两天,才完成了大概1/3的任务(被人喷了)。这个时候,我才意识到串行的任务操作是不可取的,一个直接的想法是使用多进程来同时启动多个任务(别问我问什么没有想到多线程)。于是就有了今天的话题。


正文:

        脚本如下。很简单,只有三个函数,一个设置进程池的大小,一个负责任务的进程调度,一个负责等待所有任务执行结束。具体使用方法:首先把任务代码封装成一个函数;然后把任务函数地址和函数参数传递给调度函数,调度函数就会依照设置,调度多进程完成任务;最后调用等待函数等待所有的进程完成任务。

        这里涉及到几个perl内置函数:

        1,fork 。官方文档:http://perldoc.perl.org/functions/fork.html。用于生成一个子进程。系统从当前进程复制一个进程(子进程)出来,父进程和子进程都从fork调用点开始执行。对于父进程,fork返回子进程的PID,对于子进程,fork返回0(从而可以区分当前进程是父进程还是子进程)。如果fork调用失败,fork就会返回undef。

        2,waitpid 。官方文档:http://perldoc.perl.org/functions/waitpid.html。在父进程中使用。通过这个函数探知子进程是否退出。使用子进程的PID调用该函数,waitpid(PID, wait_flag)。第一个参数如果是子进程的PID,表示等待该子进程退出;第一个参数如果是-1,表示等待所有的子进程退出(有任何一个子进程退出,就会返回)。第二参数表示如何等待子进程退出,可以阻塞等待,也可以非阻塞等待(这里使用了非阻塞等待,WNOHANG,with no hang?)。如果有子进程退出,就会返回该子进程的PID,如果没有(非阻塞时发生这种情况)就返回0,如果根本就没有子进程,就会返回-1。

        3,wait 。官方文档:http://perldoc.perl.org/functions/wait.html。没有参数,调用等待子进程退出,返回退出子进程的PID。如果根本没有子进程,就返回-1,如果有很多子进程,其中任何一个(只需要一个)子进程退出,就会触发父进程的wait()返回。所以有几个子进程退出,wait就会正确返回几次。

NOTE:perl语言本身支持多进程(但是语言本身不支持多线程,多线程需要自己实现,可以参考threads模块的用法


ProcessPool.pm

#!/usr/bin/perlpackage ProcessPool;require Exporter;use strict;use warnings;use POSIX ":sys_wait_h";use vars qw(@ISA @EXPORT $VERSION);@ISA = qw(Exporter);@EXPORT = qw(processpool_set_cnt processpool_waitforall processpool_run_task);$VERSION = 0.1;my $process_cnt = 10;my $current_cnt = 0;sub processpool_set_cnt{        if( @_ > 0 ){                $process_cnt = int($_[0]);                $process_cnt = $process_cnt == 0 ? 10 : $process_cnt;                print "set ok : process count: $process_cnt\n";        }}sub processpool_waitforall{        while($current_cnt > 0){                my $code = waitpid(-1, WNOHANG);                if( $code > 0){                        $current_cnt --;                }elsif( $code == -1 ){                        $current_cnt = 0;                }else{                        sleep(3);                }        }        print "JOBs all DONE\n";}sub processpool_run_task{        my $func = shift@_;        my @params = @_;        while($current_cnt >= $process_cnt){                my $code = waitpid(-1, WNOHANG);                if( $code > 0 ){                        $current_cnt --;                }elsif( $code == -1 ){                        print "process count ERROR\n";                        $current_cnt = 0;                }elsif( $code == 0 ){                        sleep(3);                }        }        #do job         my $pid = fork;        if( ! defined($pid) ){                print "Process create FAILED: $func @params\n";                return 1;        }        if( $pid == 0 ){                #child process                 my $res = &{$func}(@params);                exit $res;        }        $current_cnt++;        return 0;}### tail1;


        使用一个简单的测试程序,可以验证程序的正确性。


test.pl

#!/usr/bin/perl -w use lib '/home/user/myutil/base';use ProcessPool;processpool_set_cnt(3);for($i = 0; $i < 11; $i++){    processpool_run_task(\&work, $i);}processpool_waitforall();sub work{    print "work $_[0] running...\n";    sleep(30);    print "work $_[0] done\n";    return 0;}


原创粉丝点击