Python / Perl: реализация временного цикла (также с микросекундами)? - PullRequest
1 голос
/ 03 сентября 2011

Я хотел бы использовать Perl и / или Python для реализации следующего псевдокода JavaScript:

var c=0;
function timedCount()
{
  c=c+1;
  print("c=" + c);

  if (c<10)  {
    var t;
    t=window.setTimeout("timedCount()",100);
  }
}

// main:
timedCount();
print("after timedCount()");

var i=0;
for (i=0; i<5; i++) {
  print("i=" + i);
  wait(500); //wait 500 ms
}

Теперь, это особенно неудачный пример для выбора в качестве основы - но я просто не мог придумать какой-либо другой язык для его предоставления :) По сути, есть «основной цикл» и вспомогательный «цикл» ( timedCount), которые учитываются с разной скоростью: основное с периодом 500 мс (реализовано через wait), timedCount с периодом 100 мс (реализовано через setInterval). Тем не менее, JavaScript по сути однопоточный, а не многопоточный - и поэтому нет реального sleep / wait / pause или подобного ( см. Функция ожидания JavaScript - ozzu.com ), поэтому вышесказанное является, ну, псевдокодом;)

Переместив основную часть на еще одну функцию setInterval, мы можем получить версию кода, которую можно вставить и запустить в оболочке браузера, например JavaScript Shell 1.4 ( но не в терминальной оболочке, такой как EnvJS / Rhino ):

var c=0;
var i=0;
function timedCount()
{
  c=c+1;
  print("c=" + c);

  if (c<10)  {
    var t;
    t=window.setTimeout("timedCount()",100);
  }
}

function mainCount() // 'main' loop
{
  i=i+1;
  print("i=" + i);

  if (i<5)  {
    var t;
    t=window.setTimeout("mainCount()",500);
  }
}

// main:
mainCount();
timedCount();
print("after timedCount()");

... что приводит к чему-то вроде этого:

i=1
c=1
after timedCount()
c=2
c=3
c=4
c=5
c=6
i=2
c=7
c=8
c=9
c=10
i=3
i=4
i=5

... то есть основные и вспомогательные значения чередуются / чередуются / перемежаются с основным числом приблизительно каждые пять вспомогательных значений, как и предполагалось.

А теперь главный вопрос - каков рекомендуемый способ сделать это в Perl и Python соответственно?

  • Кроме того, предлагают ли Python или Perl средства для реализации вышеупомянутого с микросекундным временным разрешением в кроссплатформенном режиме?

Большое спасибо за любые ответы,
Ура!

Ответы [ 4 ]

4 голосов
/ 03 сентября 2011

Самый простой и общий способ, которым я могу думать об этом в Python, - это использовать Twisted (сетевой механизм, основанный на событиях), чтобы сделать это.

from twisted.internet import reactor
from twisted.internet import task

c, i = 0, 0
def timedCount():
    global c
    c += 1
    print 'c =', c

def mainCount():
    global i
    i += 1
    print 'i =', i

c_loop = task.LoopingCall(timedCount)
i_loop = task.LoopingCall(mainCount)
c_loop.start(0.1)
i_loop.start(0.5)
reactor.run()

В Twisted реализована высокоэффективная и стабильная реализация цикла обработки событий, называемая реактором. Это делает его однопоточным и по сути близким аналогом Javascript в вашем примере выше. Причина, по которой я бы использовал это для выполнения ваших периодических задач, описанных выше, заключается в том, что он предоставляет инструменты, позволяющие легко добавлять столько сложных периодов, сколько вам нужно.

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

3 голосов
/ 03 сентября 2011

Простая реализация Python, использующая стандартную библиотеку threading.Timer:

from threading import Timer

def timed_count(n=0):
    n += 1
    print 'c=%d' % n
    if n < 10:
        Timer(.1, timed_count, args=[n]).start()

def main_count(n=0):
    n += 1
    print 'i=%d' % n
    if n < 5:
        Timer(.5, main_count, args=[n]).start()

main_count()
timed_count()
print 'after timed_count()'

В качестве альтернативы, вы не ошибетесь, используя асинхронную библиотеку, такую ​​как twisted (продемонстрировано в этом ответе ) или gevent (есть еще немало).

2 голосов
/ 03 сентября 2011

Для Perl, для возможностей по умолчанию, в Как я сплю на миллисекунду в Perl? , указано, что:

  • sleep имеет разрешение в секунду
  • select принимает значения с плавающей запятой, десятичная часть интерпретируется как миллисекунды

А затем для более высокого разрешения можно использовать модуль Time::HiRes, например, usleep().

Если использовать возможности Perl по умолчанию, единственный способ добиться этого «многопоточного» подсчета, похоже, состоит в том, чтобы «разветвлять» сценарий и позволить каждому «разветвлению» действовать как «поток» и выполнять свой собственный подсчет.;Я видел этот подход на Perl - Как вызвать событие после задержки - Perl - и ниже приведена модифицированная версия, сделанная для отражения OP:

#!/usr/bin/env perl

use strict;
my $pid;

my $c=0;
my $i=0;

sub mainCount()
{
  print "mainCount\n";
  while ($i < 5) {
    $i = $i + 1;
    print("i=" . $i . "\n");
    select(undef, undef, undef, 0.5); # sleep 500 ms
  }
};

sub timedCount()
{
  print "timedCount\n";
  while ($c < 10) {
    $c = $c + 1;
    print("c=" . $c . "\n");
    select(undef, undef, undef, 0.1); # sleep 100 ms
  }
};


# main:
die "cant fork $!\n" unless defined($pid=fork());

if($pid) {
  mainCount();
} else {
  timedCount();
}
1 голос
/ 04 сентября 2011

Вот еще один пример Perl - без fork, с Time::HiRes с usleep (для основного) и setitimer (для вспомогательного) - однако, кажется, что setitimer необходимо перезапустить - и дажетогда, кажется, просто для запуска команд (на самом деле не ждать):

#!/usr/bin/env perl

use strict;
use warnings;

use Time::HiRes qw(usleep ITIMER_VIRTUAL setitimer);

my $c=0;
my $i=0;

sub mainCount()
{
  print "mainCount\n";
  while ($i < 5) {
    $i = $i + 1;
    print("i=" . $i . "\n");
    #~ select(undef, undef, undef, 0.5); # sleep 500 ms
    usleep(500000);
  }
};

my $tstart = 0;
sub timedCount()
{
  #~ print "timedCount\n";
  if ($c < 10) {
    $c = $c + 1;
    print("c=" . $c . "\n");

    # if we want to loop with VTALRM - must have these *continuously*
    if ($tstart == 0) {
      #~ $tstart = 1; # kills the looping
      $SIG{VTALRM} =  &timedCount;
      setitimer(ITIMER_VIRTUAL, 0.1, 0.1);
    }
  }
};


# main:

$SIG{VTALRM} =  &timedCount;
setitimer(ITIMER_VIRTUAL, 0.1, 0.1);

mainCount();

РЕДАКТИРОВАТЬ: Вот еще более простой пример с setitimer, который я не могу получить времяправильно (независимо от ITIMER_VIRTUAL или ITIMER_REAL), он просто работает максимально быстро:

use strict;
use warnings;

use Time::HiRes qw ( setitimer ITIMER_VIRTUAL ITIMER_REAL time );

sub ax() {
  print time, "\n";
  # re-initialize
  $SIG{VTALRM} = &ax; 
  #~ $SIG{ALRM} = &ax; 
}

$SIG{VTALRM} = &ax;
setitimer(ITIMER_VIRTUAL, 1e6, 1e6); 

#~ $SIG{ALRM} = &ax;
#~ setitimer(ITIMER_REAL, 1e6, 1e6); 
...