печатать каждую секунду, а также спать 10 секунд, очень 5 секунд, используя реагировать... когда-нибудь в Perl 6?

Я хочу печатать текущее время каждую секунду, а также хочу спать 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.

Не много знаете о Promise, Supply... о Perl 6, возможно ли это?

Ответ 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
...

Это гарантирует, что вы получите 5 тиков между 10-секундными паузами; выполнение этого с двумя отдельными интервальными поставками - как во многих решениях здесь - не даст никаких строгих гарантий этого, и может время от времени пропускать галочку. (Тот, который не делает, это симпатичный с rotor, который будет хорошей ставкой, если вам не нужно фактически печатать вещь "готово ко сну"). Он также свободен от состояния (переменных) и условий, что довольно приятно.

Хотя это выглядит так, как будто это может быть рекурсивно, поскольку whenever, whenever это асинхронная циклическая конструкция, она фактически не будет создавать стек вызовов вообще.

Он также полностью построен из асинхронных конструкций, и поэтому в Perl 6.d не будет - если react запускается в пуле потоков - никогда не блокировать реальный поток ОС. Таким образом, вы можете иметь тысячи таких активных. Напротив, sleep будет блокировать реальный поток, что традиционно ожидалось бы с помощью sleep, но он не очень подходит, если иначе работать с асинхронными конструкциями.

Ответ 2

Одна ошибка, которую вы делаете, заключается в том, что вы предполагаете, что запасы потеряют значения, или вы предполагаете, что они прекратят генерировать значения, пока react заблокирована. Они не будут.
Они продолжают генерировать ценности.

Вы должны также попытаться иметь код в whenever баллотироваться в качестве не хватает времени, насколько это возможно.
(Представьте, что это обработчик прерываний процессора.)
Из этого правила могут быть некоторые исключения, особенно для блоков 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;
    }
}

Пока мы занимаемся этим, почему бы просто не использовать .rotor в .interval(1) Supply?

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

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

Обратите внимание, что мы не можем просто использовать 5 => 10 потому что это объединяет их, и мы хотим, чтобы они запускались по отдельности.


Обратите внимание, что .grep также работает с поставками, поэтому мы могли бы использовать это вместо проверки значения $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

Дело в том, что два источника эффективно работают в разных потоках, поэтому не взаимодействуют друг с другом. Ваш сон только помещает нить в сон (и тот факт, что 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 потому что каждый раз, когда блок эффективно выполняется в своем собственном потоке каждую секунду. Переменные состояния позволяют нам отслеживать текущую ситуацию.

Если бы он работал на меньшем интервале, я мог бы подумать об использовании атомарных целочисленных значений вместо обычных (в случае, если код был выполнен, пока он еще работал), но этот блок никогда не должен занимать больше секунды, поэтому я не думаю это проблема.

Ответ 5

Поскольку только один whenever будет выполняться в любой момент, sleep там будет останавливать все действия, на которые нужно реагировать. Самый простой способ добиться того, что вы хотите, чтобы сделать sleep как асинхронной работы, обернув код, что whenever, start 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;
    }
}

Это дает желаемый результат, насколько я вижу.