【问题标题】:Why including a fork() call creates more loops为什么包含 fork() 调用会创建更多循环
【发布时间】:2019-08-24 13:57:39
【问题描述】:

当我在循环中包含fork() 函数调用时,循环执行的次数超过了预期的时间。为了简单起见,我只选择了两次迭代;您可以进行更多测试。

我尝试设置autoflush on,但这似乎不起作用。

use strict;
use warnings;
use IO::Handle;

$|++; #autoflush on
STDOUT->flush();

for my $i (1 .. 2) {
    print "Normal => $i\n";
}
print "\n";

for my $i (1 .. 2) {
    my $pid = fork();
    if(not defined($pid)) {
       die "cannot fork\n";
    } elsif (not $pid) {
       print "Child  => $i\n";
    } else {
       print "Parent => $i\n";
       wait();
    }
}

实际输出

Normal => 1
Normal => 2

Parent => 1
Child  => 1
Parent => 2
Child  => 2
Parent => 2
Child  => 2

预期输出

Normal => 1
Normal => 2

Parent => 1
Child  => 1
Parent => 2
Child  => 2

请帮助我理解这里出了什么问题。

【问题讨论】:

    标签: perl loops fork


    【解决方案1】:

    正如Jim Garisson's answer 中提到的,您最终会调用fork 三次,从而创建了3 个额外的进程。

    这是一个更简单的示例,它可能会帮助您了解正在发生的事情:

    say "Start";
    fork();
    say "Middle";
    fork();
    say "End";
    

    可能的输出之一是:

    Start
    Middle
    Middle
    End
    End
    End
    End
    

    初始进程说“开始”,然后分叉,因此您最终会得到两个进程,每个进程都说“中间”,然后分叉,每个进程都会创建一个额外的进程,因此您最终会得到 4 个进程说“结束” ”。

    图形表示可能有用:

     "Start"
        |
      fork()
        |
        ------------------------------
        |                            |
     "Middle"                     "Middle"
        |                            |
      fork()                       fork()
        |                            |
        ---------------              ---------------
        |             |              |             |
      "End"         "End"          "End"         "End"
    

    【讨论】:

      【解决方案2】:

      类似的问题和答案可以在https://stackoverflow.com/a/8318556/542995找到

      因为您的代码将让第一个子代开始迭代,与父代完全相同。

      所以,只是为了防止额外的循环。您可能必须 exit 子作业并将 wait(); 放置在父作业中,以在单独的循环中响应已完成的作业。而且,这将达到您的预期。

      use strict;
      use warnings;
      
      my $n = 2;
      
      for my $i (1 .. $n) {
        my $pid = fork;
        if (not $pid) {
          print "Child: $i\n";
          exit;
        } else {
          print "Parent $i\n";
        }
      }
      
      for (1 .. $n) {
         wait();
         print "Job finished\n";
      }
      

      输出:

      Parent 1
      Parent 2
      Child: 1
      Child: 2
      Job finished
      Job finished
      

      【讨论】:

        【解决方案3】:

        您最终调用fork() 三次,总共四个 进程。

        这是一种可能的执行顺序,会产生您看到的输出:

        Process 1       Process 2       Process 3       Process 4
        $i == 1
        fork----------->$i == 1
        "Parent 1"
                        "Child 1"
        $i == 2
        fork--------------------------->$i == 2
        "Parent 2"
                                        "Child 2"
                        $i == 2
                        fork--------------------------->$i == 2
                        "Parent 2"
                                                        "Child 2"
         wait
                        reaped          
                                        zombie          zombie
        

        【讨论】:

        • 对不起!无法理解。请详细说明。我应该怎么做才能防止额外的循环?
        • 经过进一步分析,我发现它比我最初想象的还要复杂。我已经更新了我的答案。
        猜你喜欢
        • 2021-05-14
        • 2018-02-22
        • 2023-02-02
        • 2017-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多