问题
I want to print the current time every second, and also want to sleep 10 seconds very 5 seconds:
react {
whenever Supply.interval(1) {
say DateTime.now.posix;
}
whenever Supply.interval(5) {
sleep 10;
say 'Sleep Done';
}
whenever signal(SIGINT) {
say "Done.";
done;
}
}
the output is not what i wanted:
1542371045
Sleep Done
1542371055
Sleep Done
1542371065
Sleep Done
1542371075
Done.
...
what i want is this:
1542371045
1542371046
1542371047
1542371048
1542371049
Sleep Done
1542371059
1542371060
1542371061
1542371062
1542371063
Sleep Done
Done.
Don't know much about Promise
, Supply
... about Raku, is this possible?
回答1:
Depending on exactly what else was needed, I'd probably write it something like this:
react {
sub sequence() {
whenever Supply.interval(1).head(5) {
say DateTime.now.posix;
LAST whenever Promise.in(10) {
say "Sleep done";
sequence();
}
}
}
sequence();
}
Which gives output like this:
1542395158
1542395159
1542395160
1542395161
1542395162
Sleep done
1542395172
1542395173
1542395174
1542395175
1542395176
Sleep done
1542395186
1542395187
1542395188
...
This will make absolutely sure you get 5 ticks out between the 10s pauses; doing it with two separate interval supplies - as in many solutions here - will not give any strict guarantees of that, and could miss a tick now and then. (One that doesn't is the cute one with rotor
, which is a good bet if you don't need to actually print the "sleep done" thing). It's also free of state (variables) and conditions, which is rather nice.
While this looks like it might be recursive, since whenever
is an asynchronous looping construct, it will not actually build up a call stack at all.
It's also fully built of asynchronous constructs, and so in Perl 6.d will not - if the react
is triggered on the thread pool - ever block a real OS thread. So you could have thousands of these active. By contrast, sleep
will block a real thread, which is what sleep
traditionally would be expected to do, but isn't such a good fit if otherwise dealing with asynchronous constructs.
回答2:
One mistake you are making is that you are assuming that supplies will lose values, or you are assuming they will stop generating values while the react
is blocked.
They won't.
They keep generating values.
You should also try to have the code in a whenever
run for as short of a time as possible.
(Pretend it is a CPU interrupt handler.)
There may be some exceptions to this rule, particularly for supply
blocks.
Using the structure that you provided, this is one way to achieve what you want:
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;
}
}
That isn't very clear.
How about we just use .rotor
instead, to skip every third interval of 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;
}
}
While we are at it, why not just use .rotor
on the .interval(1)
Supply?
react {
whenever Supply.interval(1).rotor(1 xx 4, 1 => 10) {
say DateTime.now.posix;
}
whenever signal(SIGINT) {
say "Done.";
done;
}
}
Note that we can't just use 5 => 10
because that batches them up, and we want them to be run singly.
Note that .grep
also works on Supplys, so we could have used that instead to check the $ignored
value.
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:
Maybe this can work:
loop {
react {
whenever Supply.interval(1) {
say DateTime.now.posix;
}
whenever Promise.in(5) {
done;
}
whenever signal(SIGINT) {
say "Done.";
done;
}
}
sleep 10;
}
The output is:
1542347961
1542347962
1542347963
1542347964
1542347965
1542347976 # <- 10s
1542347977
1542347978
1542347979
1542347980
1542347991 # <- 10s
回答4:
The thing is the two Supplies are effectively running in different threads so don't interact with each other. Your sleep only puts the thread it's in to sleep (and then the fact it's a 5 second interval creates another sleep anyway).
To achieve the result you're looking for I went with this which uses the single 1 second interval and a couple of flags.
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;
}
}
Note that we have to use state
variables because the whenever block is effectively executed in it's own thread each second. The state variables allow us to keep track of the current situation.
If it was running on a smaller interval I would maybe think about using atomic ints instead of normal ones (in case the code was executed while it was still running) but that block should never take more than a second to execute so I don't think it's a problem.
回答5:
Because only one whenever
will be executing at any time, the sleep
in there will be halting all handling of things to react to. The easiest way to achieve what you want, is to do the sleep
as an asynchronous job by wrapping the code of that whenever
into a start
block.
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;
}
}
This gives the desired output, as far as I can see.
来源:https://stackoverflow.com/questions/53331761/print-sth-every-second-and-also-sleep-10-seconds-very-5-seconds-using-react