免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2070 | 回复: 9
打印 上一主题 下一主题

【求助】perl 多进程通信,pipe()写入失败? [复制链接]

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2017-03-14 15:11 |只看该作者 |倒序浏览
本人小白,对pipe() 以及句柄的认识不清楚。查过一些资料仍然没有解决,特来求助。

需求:
fork多个子进程,模拟数据库查询操作,记录操作时间通过管道返回给主进程(我用的是pipe()函数)。
然后主进程进行处理。

代码如下
*****************************************************
#!/usr/bin/perl
use warnings;
use strict;
use DBI;
use Time::HiRes qw/time/;
use POSIX ":sys_wait_h";

my $count = $ARGV[0];
my $driver = "mysql";
my $database = "www";
my $dsn = "DBIdriver:database=$database";
my $userid = "root";
my $password = "123456";
my $zombies = 0;
my @READER;
my @WRITER;
my @time;
my $time_avg;

$SIG{CHLD} = 'IGNORE';


sub RESOLVE {
        my $pid;
        while (($pid = waitpid(-1, WNOHANG)) > 0) {
        print "id: $pid 子程序退出了。\n";     
        }
}

sub child {
        my $i = $_[0];
        print "I'm chid $i, my pid : $$\n";

        close $READER[$i];      
        my $time_start = time();
        my $dbh = DBI->connect($dsn, $userid, $password) or die $DBI::errstr;
        my $sth = $dbh->prepare("SELECT * FROM test"; #预处理语句
        $sth->execute() or die  $DBI::errstr;
        my $time_accomplish = time();

        #循环输出查询的数据
        while ( my @row = $sth->fetchrow_array() ){
                print join("\t", @row)."\n";

        }
        my $time_this = $time_accomplish-$time_start;
#       print "本次查询用时:".$time_this."s\n";
###############################
        select $WRITER[$i];
        $| = 1;
        print $time_this;
        $sth->finish();
        $dbh->disconnect();
        sleep(3);
        print "Child $i is done\n";
        exit 0;
###############################
}

sub parent {
        my $i = $_[0];
        print "I'm parent : My pid = $$\n";
        push @time, <$READER[$i]>;
        close $WRITER[$i];
}

for (my $i = 1; $i <= $count; $i++) {

        pipe ($READER[$i],$WRITER[$i]) or die "ipe failed: $!";
        my $pid = fork();
        if (not defined $pid){
                print "Error in fork: $!\n";
                exit 1;
        }elsif ($pid == 0) {
                child ($i);
        }else {
                parent ($i);
        }

}
#RESOLVE;
foreach my $time_each (@time){
       print "$time_each s\n";
};
print "主进程pid : $$ END\n";

exit 0

*************************************************************
执行过后输出如下:


./process.pl 2
I'm parent : My pid = 10473
I'm chid 1, my pid : 10474
I'm parent : My pid = 10473
I'm chid 2, my pid : 10475
GLOB(0xc8da1 s
GLOB(0xc8db80) s
主进程pid : 10473 END
1        huahua        18        alibaba        0.8
2        fanfan        20        nihong        2.8
3        hihi        25        yahoo        6.6
1        huahua        18        alibaba        0.8
2        fanfan        20        nihong        2.8
3        hihi        25        yahoo        6.6


************************************************************

主要的问题:

1. 输出中的这两个字符串代表什么?
        GLOB(0xc8da1 s
        GLOB(0xc8db80) s

2. 是否是因为pipe() 用法不对,其中的两个句柄可否用数组元素?
        我在child子进程中直接使用 print  $WRITER[$i] $time_this; 为什么会报错,提示 $time_this前需要操作符?
        即编译器没有把  $WRITER[$i] 这个整体当作一个 handle?

3. 句柄的写法(类型)究竟有什么限制?
        使用WRITER 是正常的,$WRITER[$i] 这种变量就不可以吗,为什么 pipe 仍然创建成功了?

4. 多个子进程和一个父进程单向通信,是否必须使用多个管道?
        如果为在for 循环里 每个管道pipe() 创建时,都写成 pipe( READER,WRITER),那么perl 实际上做了什么?
        是每次都创建一个新的pipe 吗?
        还是每次都用已经存在的pipe 来返回,并没有创建新的?
        或者是每次创建了新的,因为handle 名已经存在,所以用新的pipe 替换了 旧的,也就是说旧的pipe 被覆盖了,找不到了?

5. 如果必须用不同 handle 名来创建pipe 的话,正确的写法是什么?


以上。提前谢谢大家!~

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
2 [报告]
发表于 2017-03-14 15:12 |只看该作者
怎么出了这么多表情。。。

论坛徽章:
307
程序设计版块每周发帖之星
日期:2016-04-08 00:41:33操作系统版块每日发帖之星
日期:2015-09-02 06:20:00每日论坛发贴之星
日期:2015-09-02 06:20:00程序设计版块每日发帖之星
日期:2015-09-04 06:20:00每日论坛发贴之星
日期:2015-09-04 06:20:00每周论坛发贴之星
日期:2015-09-06 22:22:00程序设计版块每日发帖之星
日期:2015-09-09 06:20:00程序设计版块每日发帖之星
日期:2015-09-19 06:20:00程序设计版块每日发帖之星
日期:2015-09-20 06:20:00每日论坛发贴之星
日期:2015-09-20 06:20:00程序设计版块每日发帖之星
日期:2015-09-22 06:20:00程序设计版块每日发帖之星
日期:2015-09-24 06:20:00
3 [报告]
发表于 2017-03-14 19:43 |只看该作者
附加选项 -> 禁用表情

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
4 [报告]
发表于 2017-03-15 09:57 |只看该作者
回复 3# sunzhiguolu

如何呼叫斑竹啊?急。。有人能帮我解释一下吗?

论坛徽章:
307
程序设计版块每周发帖之星
日期:2016-04-08 00:41:33操作系统版块每日发帖之星
日期:2015-09-02 06:20:00每日论坛发贴之星
日期:2015-09-02 06:20:00程序设计版块每日发帖之星
日期:2015-09-04 06:20:00每日论坛发贴之星
日期:2015-09-04 06:20:00每周论坛发贴之星
日期:2015-09-06 22:22:00程序设计版块每日发帖之星
日期:2015-09-09 06:20:00程序设计版块每日发帖之星
日期:2015-09-19 06:20:00程序设计版块每日发帖之星
日期:2015-09-20 06:20:00每日论坛发贴之星
日期:2015-09-20 06:20:00程序设计版块每日发帖之星
日期:2015-09-22 06:20:00程序设计版块每日发帖之星
日期:2015-09-24 06:20:00
5 [报告]
发表于 2017-03-15 10:21 |只看该作者
回复 4# duoduoluo_z


版主 不一定每天都上线,上线了也不一定答复你,答复你也不一定给你直接答案。
别着急,再等等。这里还是有很多热心的大牛帮忙解答问题的。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
6 [报告]
发表于 2017-03-15 17:22 |只看该作者
回复 5# sunzhiguolu

恩,谢谢。输出的问题已经解决了,钻石操作符不能这么用,虽然还不知道为什么,换成readline就可以。
但是子进程开始阻塞管道。。

sub parent {
        my $i = $_[0];
        close $WRITER[$i];
        print "I'm parent : My pid = $$\n";

        while (readline $READER[$i]){
                chomp (my $line = $_);
                push @time, $line or die "push failed: $!\n";

        }
}
我感觉是上面的parent 这个函数的问题,迟迟无法返回不能开辟新的子进程。

子进程的这一部分代码(父进程在等它sleep。。),
open(STDOUT, ">&=". fileno($WRITER[$i])) or die "open writer failed: $!\n";
#       print $WRITER[$i] $time_this;
#       select $WRITER[$i];
        print $time_this,"\n" ;
        $sth->finish();
        $dbh->disconnect();
        print "sdfsdfsdf\n";
        close $WRITER[$i];
        sleep(3);
        exit 0;
*************************************************

我的问题是:
这个阻塞和哪一部分有关,是因为子进程选择了 STDOUT 输出?
还是父进程 的 while (readline) 有问题?
怎么解决。。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
7 [报告]
发表于 2017-03-15 17:23 |只看该作者
回复 5# sunzhiguolu

恩, 谢谢。输出的问题已经解决了,钻石操作符不能这么用,虽然还不知道为什么,换成readline就可以。
但是子进程开始阻塞管道。。

sub parent {
        my $i = $_[0];
        close $WRITER[$i];
        print "I'm parent : My pid = $$\n";

        while (readline $READER[$i]){
                chomp (my $line = $_);
                push @time, $line or die "push failed: $!\n";

        }
}
我感觉是上面的parent 这个函数的问题,迟迟无法返回不能开辟新的子进程。

子进程的这一部分代码(父进程在等它sleep。。),
open(STDOUT, ">&=". fileno($WRITER[$i])) or die "open writer failed: $!\n";
#       print $WRITER[$i] $time_this;
#       select $WRITER[$i];
        print $time_this,"\n" ;
        $sth->finish();
        $dbh->disconnect();
        print "sdfsdfsdf\n";
        close $WRITER[$i];
        sleep(3);
        exit 0;
*************************************************

我的问题是:
这个阻塞和哪一部分有关,是因为子进程选择了 STDOUT 输出?
还是父进程 的 while (readline) 有问题?
怎么解决。。

论坛徽章:
0
8 [报告]
发表于 2017-03-15 23:02 |只看该作者
你本来有两个问题都是与file handle有关,不论diamond operator还是print都不能直接用$array[$i]。diamond operator可以改成readline(),也可以用一个scalar先从$array[$i]赋值然后用diamond operator操作这个scalar。print的话需要加花括号,比如print { $array[$i] } "foo" 这样子。

另外你可能需要在writer上打开autoflush模式。父进程需要waitpid。你可以先写一个简单的只有一对父子进程,也不涉及DBI的例子来调试。

还有我想说的是,如果只是要完成你现在的这样的功能的话,都不需要自己用pipe。你可以直接用Parallel::ForkManager,一样可以多进程并发,可以收集数据,绝对比自己用pipe省事。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
9 [报告]
发表于 2017-03-16 10:47 |只看该作者
回复 8# zhouzhen1

感谢!大括号很好用。
现在的问题是 readline() 的阻塞问题:要给 handle 里扔什么符号才能告诉 readline() handle里的内容结束了呢?

forkmanager 之前看过。只是不想用多余的模块,自己写比较好。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-04-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-26 06:20:00
10 [报告]
发表于 2017-03-16 11:11 |只看该作者
回复 8# zhouzhen1

另外handle->autoflush(1) 不就是 $| = 1; 吗?这个好像不影响吧。


您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP