2013-03-23 19 views
7

Devo inviare messaggi ripetuti da un elenco in modo rapido e preciso. Una lista deve inviare i messaggi ogni 100 ms, con una finestra di +/- 10ms. Ho provato ad usare il codice qui sotto, ma il problema è che il timer aspetta i 100 ms, e quindi tutto il calcolo deve essere fatto, facendo cadere il timer dalla finestra accettabile.Timer di ripetizione Python veloce e preciso

Basta diminuire l'attesa è un hack incasinato e inaffidabile. C'è un blocco attorno al loop dei messaggi nel caso in cui l'elenco venga modificato durante il ciclo.

Pensieri su come ottenere Python per inviare messaggi costantemente intorno ai 100 ms? Grazie

from threading import Timer 
from threading import Lock 

class RepeatingTimer(object): 
    def __init__(self,interval, function, *args, **kwargs): 
     super(RepeatingTimer, self).__init__() 
     self.args = args 
     self.kwargs = kwargs 
     self.function = function 
     self.interval = interval 
     self.start() 

    def start(self): 
     self.callback() 

    def stop(self): 
     self.interval = False 

    def callback(self): 
     if self.interval: 
      self.function(*self.args, **self.kwargs) 
      Timer(self.interval, self.callback,).start() 

def loop(messageList): 
    listLock.acquire() 
    for m in messageList: 
     writeFunction(m) 
    listLock.release() 


MESSAGE_LIST = [] #Imagine this is populated with the messages 
listLock = Lock() 
rt = RepeatingTimer(0.1,loop,MESSAGE_LIST) 
#Do other stuff after this 

Capisco che la funzione write causerà qualche ritardo, ma non superiore ai 10ms consentiti. In sostanza, ho bisogno di chiamare la funzione ogni 100ms per ogni messaggio. Il messaggista è piccolo, in genere meno degli elementi.

La prossima sfida è quella di avere questo lavoro con ogni 10ms, +/- 1ms: P

+0

'lavoro con ogni 10ms, +/- 1ms' buona fortuna con quella :( –

risposta

0

Conservare l'ora di inizio. Invia il messaggio Ottieni l'ora di fine. Calcola timeTaken = end-start. Converti in secondi FP. Sonno (0,1-timeTaken). Torna indietro.

8

Sì, la semplice attesa è disordinata e ci sono alternative migliori.

Prima di tutto, è necessario un timer ad alta precisione in Python. Esistono alcune alternative e, a seconda del sistema operativo, è possibile scegliere the most accurate one.

In secondo luogo, è necessario essere a conoscenza dello basics preemptive multitasking e capire che non esiste una funzione di precisione sleep e che la sua risoluzione effettiva sarà diversa dal sistema operativo al sistema operativo. Ad esempio, se stiamo parlando di Windows, the minimal sleep interval might be around 10-13 ms.

E in terzo luogo, ricordare che è sempre possibile attendere un intervallo di tempo molto accurato (presupponendo che si disponga di un timer ad alta risoluzione), ma con un compromesso di carico elevato della CPU. La tecnica si chiama busy waiting:

while(True): 
    if time.clock() == something: 
     break 

Quindi, la soluzione reale è quello di creare un timer ibrido. Utilizzerà la normale funzione sleep per attendere la maggior parte dell'intervallo principale, quindi inizierà a sondare il timer ad alta precisione nel loop, mentre esegue il trucco sleep(0). Sleep(0) (in base alla piattaforma) attenderà il minor tempo possibile, rilasciando il resto della porzione di tempo rimanente ad altri processi e passando al contesto della CPU. Ecco un rilevante discussion.

L'idea è descritta dettagliatamente nell'articolo Timing in Win32 di Ryan Geiss. È in C e per Windows API, ma i principi di base si applicano anche qui.

+2

non è più sicuro per controllare time.clock()> = qualcosa che penso? che c'è il rischio che il "time.clock()" salti sopra "qualcosa" un po ' – vac

+0

Non farti ingannare dall'etichetta "busy waiting" di fantasia ;-). È una pessima pratica e deve essere evitata ogni volta che è possibile. – jjmontes

0

provare questo:

#!/usr/bin/python 
import time; # This is required to include time module. 
from threading import Timer 

def hello(start, interval, count): 
    ticks = time.time() 
    t = Timer(interval - (ticks-start-count*interval), hello, [start, interval, count+1]) 
    t.start() 
    print "Number of ticks since 12:00am, January 1, 1970:", ticks, " #", count 

dt = 1.25 # interval in sec 
t = Timer(dt, hello, [round(time.time()), dt, 0]) # start over at full second, round only for testing here 
t.start()