2009-08-20 5 views
15

Mi sono imbattuto in un test unitario che si interrompe a intermittenza perché il tempo trascorso non è quello che mi aspetto che sia.Quanto è accurato Thread.Sleep (TimeSpan)?

Un esempio di ciò che questo test si presenta come è:

Stopwatch stopwatch = new Stopwatch(); 
stopwatch.Start(); 

TimeSpan oneSecond = new TimeSpan(0, 0, 1); 

for(int i=0; i<3; i++) 
{ 
    Thread.Sleep(oneSecond); 
} 

stopwatch.Stop(); 

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, 2999); 

Il più delle volte questo passa, ma non è riuscita almeno in un'occasione riuscita perché:

previsto: maggiore o uguale a 2999 ma era: 2998

Non capisco come potrebbe essere inferiore a 3 secondi. Esiste un problema di accuratezza con Thread.Sleep o forse Cronometro di cui non sono a conoscenza?

Proprio come un aggiornamento ad alcune delle domande seguenti. Lo scenario che viene testato unitariamente è una classe che consente di chiamare un metodo per eseguire qualche azione e, se fallisce, aspetta un secondo e richiama quel metodo. Il test mostrato sopra è solo un'approssimazione di ciò che sta accadendo.

Dire che volevo chiamare un metodo DoSomething() ... ma nel caso di un'eccezione lanciata da DoSomething() voglio poter riprovare a chiamarlo fino a un massimo di 3 volte ma attendere 1 secondo tra ogni tentativo. Lo scopo del test unitario, in questo caso, è verificare che quando abbiamo richiesto 3 tentativi con 1 secondo di attesa tra ogni tentativo, il tempo totale impiegato è maggiore di 3 secondi.

+0

Solo per curiosità, perché si dispone di un test dell'unità che testa Thread.Sleep() in questo modo? Immagino che in realtà stai testando per vedere se qualche altra attività si completa in meno di 3 secondi, ma perché non chiamare semplicemente Thread.Sleep (3000)? – MusiGenesis

+0

@MusiGenesis Ho aggiornato la domanda per cercare di chiarire un po 'le cose. – mezoid

+0

Solo per informazioni. Da quando ho scoperto questo piccolo codice, lo uso ovunque: http://www.codeproject.com/KB/cs/highperformancetimercshar.aspx – sabiland

risposta

16

Il thread condivide ora CPU con altri thread. Lo Sleep terminerà non appena sarà di nuovo il tuo turno e il kernel noterà che il tempo di sonno è trascorso, quindi non è così preciso.

Il carico della CPU, le priorità del processo, il numero di thread simultanei, anche da altri processi, avranno effetto su di esso.

+4

ma se termina non appena il kernel nota che il tempo di sonno è scaduto, come va è possibile che impieghi meno tempo di quanto mi aspetti? 3010 ms è comprensibile e accettabile nel mio caso si sta arrestando prima di raggiungere il tempo richiesto ... anche se di 1-2 ms – mezoid

+2

L'implementazione probabilmente imposta un tempo di attivazione di alcuni millisecondi prima dell'orario specificato. Se così non fosse, * sempre * sveglierà più tardi. Impostando il tempo in precedenza, è meglio trovare un posto vicino. –

+0

@mezoid: non penso che le architetture dei PC siano fatte per avere molta accuratezza temporale. I vecchi PC "spuntano" solo 18.2 volte al secondo, quindi l'orologio e il "tick count" usati vengono aggiornati solo ogni 52ms. Credo che al giorno d'oggi debba essere molto più veloce, ma non sono ancora sicuro di quanto sia accurato. – Havenard

8

Thread. Il sonno non è destinato all'uso per la risveglio di precisione. In realtà, l'architettura Windows stessa non è pensata per questo tipo di cose.

1

In un'applicazione dove volevo dormire per atleast x millisecondi, ho usato un po 'di codice simile a:

public void Sleep(int milliseconds) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 

    while (stopwatch.ElapsedMilliseconds < milliseconds) 
    { 
     int timeout = milliseconds - stopwatch.ElapsedMilliseconds; 
     Thread.Sleep(timeout >= 0 ? timeout : 0); 
    } 

    stopwatch.Stop(); 
} 

In risposta a quanto Thread.Sleep precisa è, non è preciso a tutti. Penso che la risoluzione sia da qualche parte intorno ai 10ms. Non è garantito fare molto di tutto tranne che "approssimativamente" così a lungo.

+0

Perché non solo Thread.Sleep (millisecondi + 100)? – MusiGenesis

+0

Hai qualche riferimento a cui potresti indirizzarmi sulla risoluzione? Se è sicuramente 10 ms, potrei cambiare da 2999 a 2990 ..... – mezoid

+1

Non c'è una risoluzione difficile e veloce a causa di cose che altre persone hanno notato (come il carico del processore che lo influenza). Fondamentalmente si tratta di quando il thread riceve nuovamente la priorità per iniziare l'esecuzione. Le prove aneddotiche che ho visto intorno a SO in diversi punti hanno indicato una media di circa 10 ms. –

2

La sospensione del filo e la temporizzazione/strozzatura sono cose molto diverse e devono essere trattate in modo appropriato. Dormire un thread è un compito generale, che consente al sistema di dare ad altri thread e processi la possibilità di eseguire senza essere specifico su di esso. D'altra parte, la limitazione di un'applicazione o delle attività di pianificazione che richiedono una tempistica accurata dovrebbe essere eseguita con un timer esplicito.

tenere a mente, se avete bisogno di processi time-accurate o la sincronizzazione, si avrà un momento difficile raggiungere questo con un processo normale in finestre. Avresti bisogno di utilizzare le priorità in tempo reale di Windows per raggiungere con successo tempismo o limitazione, dato che Windows può dormire qualsiasi thread in qualsiasi momento se è preceduto da un altro thread.

+0

Sono d'accordo, l'intenzione per Thread.Sleep riguarda le priorità dei thread e non ha un timing preciso. Fino a quando non avremo un clock di elaborazione di cesio nel nostro PC, sfortunatamente, il tempo preciso (assoluto) non sarà disponibile. : P http://en.wikipedia.org/wiki/Caesium – Russell

+0

Beh, mi auguro che la maggior parte delle persone non abbia bisogno di cronometraggio accurato all'orologio atomico. : P Penso che l'orologio nella maggior parte delle CPU sia abbastanza preciso per la maggior parte delle applicazioni oltre la misurazione delle vibrazioni atomiche. : D – jrista

2

sperimentare rapidamente, mi accorgo che un frammento di codice come ...

fare {Debug.WriteLine (DateTime.Now.TimeOfDay.TotalMilliseconds.Accordare()); } while (1);

visualizza lo stesso numero più volte, poi salta a un nuovo numero visualizzati più volte, ecc Il divario tra questi insiemi di numeri è costantemente 15.625ms che ho notato è 1000/64.

Sembra che i timer di Windows avere una granularità di 1/64 di secondo. Se hai bisogno di qualcosa di meglio allora sento il tuo dolore, ma questa è la struttura che devi adattare. (Windows non è un sistema operativo in tempo reale difficile e non pretende di essere).

0

Forse non puoi fare affidamento sull'intervallo di tempo per scoprire se sei riuscito. Contare meglio il numero di tentativi e valutare in base a questo:

int tries; 

for(tries=0; tries<3; tries++) 
{ 
    Thread.Sleep(oneSecond); 
} 

Assert.GreaterOrEqual(tries, 3);