2011-08-25 15 views
14

Ho sempre notato che le persone hanno reazioni diverse ai diversi modi di creare un timer in UNIX.Il modo migliore per fare un timer in UNIX

Conosco un paio di modi diversi che ho potuto fare un evento eseguire ogni X secondi all'interno di UNIX:

  • si può fare un thread di polling - pthread_t con posti letto;
  • si può fare un select() con un timeout;
  • e credo che sia possibile utilizzare il sistema operativo in pochi altri modi.

Qualcuno può fornire qualche codice di esempio con il "migliore" o il modo più efficiente per fare un timer e una descrizione del perché è il migliore? Mi piacerebbe usare il meccanismo più efficiente per questo, ma non sono sicuro di quale sia!

Per i nostri scopi, basta far finta che stai stampando "Ciao Mondo!" una volta ogni 10 secondi.

NOTA: Non ho TR1/Boost/ecc. su questo sistema, quindi ti preghiamo di tenerlo in linea con le chiamate di sistema C/C++ e UNIX. Ci scusiamo per non averlo menzionato per la prima volta :)

+0

programmazione a thread singolo è più facile che un concorrente, quindi se si può garantire che le chiamate normali non superano l'intervallo, mi piacerebbe andare con un semplice timeout (come con 'select()'). Avrai bisogno del multithreading, tuttavia, se le chiamate stesse impiegano più tempo e il tuo timer deve sparare nuovamente prima che le chiamate ritornino. –

+0

Il sistema utilizza già molto il multithreading, quindi non sarebbe un problema. Lanciati come risposta :) –

+0

Hm, non ho davvero nulla da dire sulla domanda vera e propria. Ho usato boost.asio per le mie esigenze di temporizzazione prima (con il ciclo ioservice in esecuzione in un thread separato), ma non riesco a parlare con i vantaggi comparativi dei vari approcci ... –

risposta

5

Dipende da cosa si vuole fare. Un semplice sonno sarà sufficiente per il tuo banale esempio di aspettare 10 secondi tra "Ciao", dal momento che potresti anche sospendere il thread corrente fino a quando il tuo tempo è scaduto.

Le cose diventano più complicate se il filo è in realtà facendo qualcosa mentre si è in attesa. Se stai rispondendo alle connessioni in entrata, utilizzerai già select in questo caso un timeout sulla tua istruzione select è più adatto per le tue pulizie.

Se si sta elaborando roba in un loop stretto, si potrebbe prelevare regolarmente un orario di inizio per vedere se i vostri 10 secondi sono in su.

alarm con un gestore di segnale appropriato funziona altrettanto bene, ma ci sono forti limiti a ciò che si può fare in un gestore di segnale. Il più delle volte, ha comportato l'impostazione di una bandiera che dovrà essere interrogata ogni tanto.

In breve, si tratta di come il thread sta elaborando gli eventi.

2

Se il tuo programma è già threadato, usare un sondaggio è semplice ed efficace.

Se il programma non è già threadato, diventa più complicato. Puoi usare un processo separato per fare il lavoro? In tal caso, quindi utilizzare multi-elaborazione anziché multi-threading.

Se non è possibile utilizzare un thread di controllo autonomo (processo o thread), dipende da come è organizzato il programma, motivo per cui esistono molte alternative preferite (sono preferibili in circostanze diverse). Dipende anche dall'accuratezza dei tempi richiesti. Con intervalli di più secondi e senza requisiti di risposta in tempo reale, è possibile utilizzare un allarme e il gestore SIGALRM può impostare un flag e il ciclo di elaborazione principale può monitorare la bandiera in punti convenienti e sensibili e svolgere l'attività. Questo è un po 'disordinato (perché i segnali sono disordinati), ma senza una descrizione più dettagliata dei requisiti e dei vincoli, è difficile sapere cos'altro suggerire.

Quindi, in sintesi, non esiste un'unica soluzione universalmente ottimale poiché diversi programmi sono scritti in stili diversi e con vincoli diversi.

+0

+1: Molto bello .. –

+0

Oltre ai costi nella dipendenza dalla libreria, l'aggiunta di più elaborazioni a un'applicazione è ** molto più lavoro ** per ottenere giusto che virare su multi-threading. I bug di modulo nell'implementazione di pthread, creando un thread da un punto nell'applicazione dovrebbero essere completamente trasparenti al resto dell'applicazione. D'altra parte, l'aggiunta di un processo richiede l'uso di 'SIGCHLD', pid e attesa/uscita dalla gestione dello stato, ecc. –

3

Utilizzare un ciclo di eventi-driven come Boost.Asio

#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 
#include <boost/date_time/posix_time/posix_time.hpp> 

class Timer 
{ 
public: 
    Timer(
      boost::asio::io_service& io_service 
     ) : 
     _impl(io_service) 
    { 
     this->wait(); 
    } 

private: 
    void wait() 
    { 
     _impl.expires_from_now(boost::posix_time::seconds(10)); 
     _impl.async_wait(
       boost::bind(
        &Timer::handler, 
        this, 
        _1 
        ) 
       ); 
    } 

    void handler(
      const boost::system::error_code& error 
      ) 
    { 
     if (error) { 
      std::cerr << "could not wait: " << boost::system::system_error(error).what() << std::endl; 
      return; 
     } 

     std::cout << "Hello World!" << std::endl; 

     this->wait(); 
    } 

private: 
    boost::asio::deadline_timer _impl; 
}; 

int main() 
{ 
    boost::asio::io_service io; 

    Timer t(io); 

    io.run(); 

    return 0; 
} 

accumulo & run:

stackoverflow samm$ ./a.out 
Hello World! 
Hello World! 
Hello World! 
^C 
stackoverflow samm$ 
+1

Mi piace quella risposta, ma sfortunatamente non ho TR1/Boost disponibile per me - in più vorrei qualcosa questo è OS o straight-C, quindi è più portatile. L'ha votato anche se è una buona soluzione :) –

+0

Nice; e questo può facilmente essere multithreading, se necessario, mettendo 'run()' in un thread separato! –

+1

@ w00te: Se vuoi qualcosa "straight C", non taggare la tua domanda "C++"! –

1

Le opzioni su come eseguire un timer variano in facilità di utilizzo, portabilità, precisione, ugliness (stato globale/segnali) e idoneità per le applicazioni in tempo reale. Personalmente, per il meglio di tutti i mondi, ti consiglio di stampare il tuo timer con un thread e di utilizzare clock_nanosleep con il flag TIMER_ABSTIME e la sorgente di clock CLOCK_MONOTONIC. In questo modo puoi evitare errori accumulati e discontinuità dall'impostare l'ora del sistema, e puoi contare sul superamento di te stesso. In teoria, POSIX timer_create con consegna SIGEV_THREAD dovrebbe darti le stesse proprietà, ma l'implementazione di glibc è davvero pessima e non può garantire che non perderà gli eventi di scadenza del timer sotto carico pesante.

+0

Non sapevo che "SIGEV_THREAD" fosse considerato inaffidabile. Viene dall'esperienza personale o c'è una segnalazione di bug da qualche parte? – cnicutar

+0

Viene dalla lettura della fonte (inclusi i commenti che documentano l'errore). Chiama 'malloc' e' pthread_create' per ogni consegna, e se uno dei due fallisce, la consegna andrà persa e non verrà riportata in 'timer_getoverrun'. L'implementazione più sicura è quella di avere un thread persistente per il timer che viene riutilizzato per ogni consegna, ma ciò richiede alcuni hack e un'interazione con l'implementazione dei thread in modo che 'pthread_exit' e' pthread_cancel' funzionino e nascondano il fatto che il thread viene riutilizzato. –

+0

Grazie per le informazioni, apprezzate :-) – cnicutar

1

sleep() è il modo più semplice. usleep() esiste su alcune varianti. Se si desidera una precisione inferiore al secondo, selezionare (NULL, NULL, NULL & timeout) è il più portatile, a meno che non si scriva le proprie routine specifiche del sistema operativo. Il metodo select() ha il vantaggio che il timeout viene aggiornato per riflettere la quantità di tempo rimanente se la chiamata è stata interrotta da un segnale. Puoi riprendere l'attesa se necessario.

In realtà, tutti i metodi sono equivalenti; il sistema operativo interrompe la pianificazione del thread per il periodo di timeout.

Il metodo SIGALARM suggerito da Blagovestus è (un po ') non portabile, anche se dovrebbe funzionare su Linux.

0

Se devi solo inviare "Hello world" una volta ogni 10 secondi, suppongo che dormiranno e dormiranno i nanosleep. Se le tue attività sono in qualche modo più importanti, puoi dare un'occhiata a RealTime Linux. Lì (www.ee.nmt.edu/~rison/ee352_fall09/Getting_Started_with_RTLinux.pdf) ci hanno messo una buona guida. Quando si utilizza RTLinux, è possibile utilizzare pthread_make_periodic_np per eseguire periodicamente un'attività, anche se si è incaricati di verificare che le condizioni in tempo reale siano soddisfatte.

1

Questa è una risposta tardiva, ma volevo aggiungere la mia implementazione di un timer. È un timer a thread e ha bisogno di un po 'di lavoro, con il wrapping di mutex e pthreads nelle proprie classi (mancanza di RAII al riguardo). Inoltre, non è multipiattaforma poiché utilizza il metodo gettimeofday().

Il codice utilizza un timer che chiama un oggetto CallBack. L'intero sorgente può essere visto a

http://matthewh.me/playground/browser/branches/C%2B%2B/c%2B%2B_callbacks

+1

Bene, ora sono nel mondo C#, ma la risposta è ancora apprezzata :) È un bel codice, quindi sono sicuro che sarà utile per chi guarda questo domanda. –

Problemi correlati