【问题标题】:print sth every second, and also sleep 10 seconds very 5 seconds using react ... whenever in Perl 6?每秒打印一次,并且在 Perl 6 中使用 react 睡 10 秒非常 5 秒?
【发布时间】:2018-11-16 05:06:35
【问题描述】:

我想每秒打印当前时间,还想睡10秒非常5秒:

react {
    whenever Supply.interval(1) {
        say DateTime.now.posix;
    }

    whenever Supply.interval(5) {
        sleep 10;
        say 'Sleep Done';
    }

    whenever signal(SIGINT) {
        say "Done.";
        done;
    }
}

输出不是我想要的:

1542371045
Sleep Done
1542371055
Sleep Done
1542371065
Sleep Done
1542371075
Done.
...

我想要的是这个:

1542371045
1542371046
1542371047
1542371048
1542371049 
Sleep Done
1542371059
1542371060
1542371061  
1542371062 
1542371063         
Sleep Done
Done.

不太了解PromiseSupply...关于Raku,这可能吗?

【问题讨论】:

  • 是否应该在这 10 秒的休息时间继续进行每秒的时间报告?是否应该每 5 秒触发 10 秒睡眠,所以它报告它每 5 秒睡眠一次?
  • 我认为基本的误解是一次只会执行 一个 whenever 块。这实际上是记录不足的,所以我将对此进行尝试。
  • @JonathanWorthington 感谢您的关注。 1、是否应该在这10秒的休息时间内继续进行每秒的时间报告?不,持续 10 秒的休息时间,我不希望它继续报告时间。 2、是不是应该每5秒触发10秒睡眠,所以每5秒就报告睡了一次?是的。
  • 如果你不想让它在睡眠时报告,它每次睡眠 10 秒,并且你希望它每 5 秒报告它完成睡眠,这意味着在任何时候有两个10 秒睡眠处于活动状态,根本不应该报告时间;我误会了吗?
  • @timotimo 是的,你是对的。睡眠时,它不报告时间。

标签: raku


【解决方案1】:

根据具体需要什么,我可能会这样写:

react {
    sub sequence() {
        whenever Supply.interval(1).head(5) {
            say DateTime.now.posix;
            LAST whenever Promise.in(10) {
                say "Sleep done";
                sequence();
            }
        }
    }
    sequence();
}   

输出如下:

1542395158
1542395159
1542395160
1542395161
1542395162
Sleep done
1542395172
1542395173
1542395174
1542395175
1542395176
Sleep done
1542395186
1542395187
1542395188
...

这将绝对确保您在 10 秒停顿之间获得 5 个滴答;使用两个单独的间隔电源进行此操作 - 就像这里的许多解决方案一样 - 不会对此提供任何严格的保证,并且有时可能会错过一个滴答声。 (一个没有的是可爱的rotor,如果你不需要实际打印“睡眠完成”的东西,这是一个不错的选择)。它也没有状态(变量)和条件,这相当不错。

虽然这个看起来可能是递归的,但由于whenever 是一个异步循环结构,它实际上根本不会构建调用堆栈。

它也完全由异步结构构建,因此在 Perl 6.d 中,如果在线程池上触发 react,则不会阻塞真正的 OS 线程。所以你可以有数千个这样的活动。相比之下,sleep 将阻塞一个真正的线程,这是 sleep 传统上所期望的,但如果以其他方式处理异步构造,则不是那么合适。

【讨论】:

  • 我可能已经尝试过递归实现,只是我想继续使用原来的.interval(1) Supply。
  • 非常感谢您的回答!虽然并发很难理解,但 Perl 6 使困难的事情成为可能。
【解决方案2】:

您犯的一个错误是您假设供应品将失去价值,或者您假设它们将在 react 被阻止时停止生成价值。 他们不会。
他们不断创造价值。

您还应该尝试让whenever 中的代码运行尽可能短的时间。
(假设它是一个 CPU 中断处理程序。)
此规则可能存在一些例外情况,尤其是对于 supply 块。

使用您提供的结构,这是实现您想要的一种方式:

 react {
    # Are we ignoring the interval(1) values?
    my Bool:D $ignore = False;

    # The sleeping status of interval(5).
    my Promise:D $sleep .= kept;

    whenever Supply.interval(1) {
        # Skip if it is supposed to be blocked.
        next if $ignore;

        say DateTime.now.posix;
    }

    # First one runs immediately, so skip it.
    whenever Supply.interval(5).skip {
        # Don't run while the “sleep” is pending.
        next unless $sleep.status; # Planned

        if $ignore {
            $ignore = False;
            say 'Sleep Done';
        } else {
            $ignore = True;
            # Must be less than the multiple of 5 we want
            # otherwise there may be a race condition.
            $sleep = Promise.in(9);
        }
    }

    whenever signal(SIGINT) {
        say "Done.";
        done;
    }
}

这不是很清楚。
不如我们只使用.rotor 来跳过每三个 5 的间隔?

react {
    my Bool:D $ignore = True;

    # Note that first one runs immediately. (no .skip)
    # We also want it to always be a few milliseconds before
    # the other Supply, so we put it first.
    # (Should have done that with the previous example as well.)
    whenever Supply.interval(5).rotor(1, 1 => 1) {
        $ignore = !$ignore;
    }

    whenever Supply.interval(1) {
        next if $ignore;

        say DateTime.now.posix;
    }

    whenever signal(SIGINT) {
        say "Done.";
        done;
    }
}

当我们这样做的时候,为什么不在.interval(1) Supply 上使用.rotor

react {
    whenever Supply.interval(1).rotor(1 xx 4, 1 => 10) {
        say DateTime.now.posix;
    }

    whenever signal(SIGINT) {
        say "Done.";
        done;
    }
}

请注意,我们不能只使用5 => 10,因为这会将它们分批处理,我们希望它们单独运行。


请注意,.grep 也适用于 Supply,因此我们可以使用它来检查 $ignored 的值。

react {
    my Bool:D $ignore = True;

    whenever Supply.interval(5).rotor(1, 1 => 1) {
        $ignore = !$ignore;
    }

    whenever Supply.interval(1).grep({ !$ignore }) {
        say DateTime.now.posix;
    }

    whenever signal(SIGINT) {
        say "Done.";
        done;
    }
}

【讨论】:

    【解决方案3】:

    也许这可以工作:

    loop {
        react {
            whenever Supply.interval(1) {
                say DateTime.now.posix;
            }
    
            whenever Promise.in(5) {
                done;
            }
    
            whenever signal(SIGINT) {
                say "Done.";
                done;
            }
        }
        sleep 10;
    }
    

    输出是:

    1542347961
    1542347962
    1542347963
    1542347964
    1542347965
    1542347976 # <- 10s
    1542347977
    1542347978
    1542347979
    1542347980
    1542347991 # <- 10s
    

    【讨论】:

      【解决方案4】:

      问题是这两个 Supplies 有效地在不同的线程中运行,所以不要相互交互。您的 sleep 只会将其所在的线程置于睡眠状态(然后事实上它是 5 秒的间隔会创建 另一个 睡眠)。

      为了达到您正在寻找的结果,我使用了这个,它使用单个 1 秒间隔和几个标志。

      react { 
          whenever Supply.interval(1) { 
              state $slept = False;  
              state $count = 0;
              if $count >= 0 { 
                  if $slept { 
                      say "Sleep Done"; 
                      $slept = False 
                  } 
                  say DateTime.now.posix; 
              } 
              $count++; 
              if ( $count == 5 ) { 
                  $count = -9; 
                  $slept = True
              }
          }
          whenever signal(SIGINT) {
              say "Done.";
              done;
          }
      }
      

      请注意,我们必须使用state 变量,因为whenever 块每秒在它自己的线程中有效执行。状态变量使我们能够跟踪当前情况。

      如果它以更小的间隔运行,我可能会考虑使用原子整数而不是普通整数(以防代码在它仍在运行时执行)但该块的执行时间不应超过一秒,所以我不要认为这是一个问题。

      【讨论】:

        【解决方案5】:

        因为只有 一个 whenever 会在任何时候执行,所以其中的sleep 将停止所有要响应的事情的处理。实现您想要的最简单的方法是将sleep 作为异步作业,将whenever 的代码包装到start 块中。

        react {
            whenever Supply.interval(1) {
                say DateTime.now.posix;
            }
        
            whenever Supply.interval(5) {
                start {
                    sleep 10;
                    say 'Sleep Done';
                }
            }
        
            whenever signal(SIGINT) {
                say "Done.";
                done;
            }
        }
        

        据我所知,这给出了所需的输出。

        【讨论】:

        • 感谢您的关注。但时间报告是连续的。也许很难用我的英语描述这个问题。所以我写了一个报告不连续时间的答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-06
        • 1970-01-01
        • 2010-10-28
        • 2013-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多