Domanda

Su Windows ho un problema che non ho mai riscontrato su Unix.Ecco come far dormire un thread per meno di un millisecondo.Su Unix in genere hai una serie di scelte (sleep, usleep e nanosleep) per soddisfare le tue esigenze.Su Windows, invece, c'è solo Sonno con granularità al millisecondo.

Su Unix, posso usare use the select chiamata di sistema per creare una sospensione di microsecondi che è piuttosto semplice:

int usleep(long usec)
{
    struct timeval tv;
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, 0, &tv);
}

Come posso ottenere lo stesso risultato su Windows?

È stato utile?

Soluzione 18

Su Windows l'uso di select ti obbliga a includere il file Winsock libreria che deve essere inizializzata in questo modo nella tua applicazione:

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);

E poi la selezione non ti permetterà di essere chiamato senza alcuna presa, quindi devi fare qualcosa in più per creare un metodo microsleep:

int usleep(long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, &dummy, &tv);
}

Tutti questi metodi usleep creati restituiscono zero in caso di successo e diverso da zero in caso di errori.

Altri suggerimenti

Ciò indica una comprensione errata delle funzioni del sonno.Il parametro che passi è a minimo tempo per dormire.Non esiste alcuna garanzia che il thread venga riattivato esattamente dopo l'orario specificato.In effetti, i thread non si "svegliano" affatto, ma vengono piuttosto scelti per l'esecuzione dallo scheduler.Lo scheduler potrebbe scegliere di attendere molto più a lungo della durata di sospensione richiesta per attivare un thread, soprattutto se in quel momento è ancora attivo un altro thread.

Come dice Joel, non puoi "dormire" in modo significativo (cioèrinunciare alla CPU programmata) per periodi così brevi.Se si desidera ritardare per un breve periodo, è necessario girare, controllando ripetutamente un timer ad alta risoluzione adeguata (ad es.il "timer delle prestazioni") e sperando che qualcosa di alta priorità non ti prenda comunque il sopravvento.

Se ti interessa davvero che ritardi accurati di tempi così brevi, non dovresti utilizzare Windows.

Utilizza i timer ad alta risoluzione disponibili in winmm.lib.Vedere Questo per un esempio.

Sì, devi comprendere i quanti temporali del tuo sistema operativo.Su Windows, non otterrai nemmeno tempi di risoluzione di 1 ms a meno che non cambi il quanto temporale in 1 ms.(Utilizzando ad esempio timeBeginPeriod()/timeEndPeriod()) Ciò non garantirà comunque nulla.Anche un piccolo carico o un singolo driver di dispositivo scadente rovineranno tutto.

SetThreadPriority() aiuta, ma è piuttosto pericoloso.I driver di dispositivo scadenti possono ancora rovinarti.

Hai bisogno di un ambiente informatico ultra-controllato per far funzionare queste brutte cose.

#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}

sì, utilizza alcune funzioni del kernel non documentate, ma funziona molto bene, utilizzo SleepShort(0.5);in alcuni dei miei thread

Se vuoi così tanta granularità sei nel posto sbagliato (nello spazio utente).

Ricorda che se sei nello spazio utente il tuo tempo non è sempre preciso.

Lo scheduler può avviare il thread (o l'app) e pianificarlo, quindi dipendi dallo scheduler del sistema operativo.

Se cerchi qualcosa di preciso devi andare:1) nello spazio del kernel (come i conducenti) 2) Scegli un RTO.

Ad ogni modo se stai cercando un po 'di granularità (ma ricorda il problema con lo spazio utente), guarda la funzione QueryPerformanceCounter e la funzione QueryPerformance -Frequency in MSDN.

Come hanno sottolineato diverse persone, il sonno e altre funzioni correlate dipendono per impostazione predefinita dal "segno di spunta del sistema".Questa è l'unità di tempo minima tra le attività del sistema operativo;lo scheduler, ad esempio, non funzionerà più velocemente di così.Anche con un sistema operativo realtime, il tick del sistema solitamente non è inferiore a 1 ms.Sebbene sia personalizzabile, ciò ha implicazioni per l'intero sistema, non solo per la funzionalità di sospensione, poiché lo scheduler verrà eseguito più frequentemente e aumenterà potenzialmente il sovraccarico del sistema operativo (quantità di tempo per l'esecuzione dello scheduler, rispetto aquantità di tempo in cui un'attività può essere eseguita).

La soluzione a questo problema è utilizzare un dispositivo clock esterno ad alta velocità.La maggior parte dei sistemi Unix ti consentirà di specificare i tuoi timer e un orologio diverso da utilizzare, rispetto all'orologio di sistema predefinito.

Cosa stai aspettando che richieda tanta precisione?In generale se tu Bisogno per specificare il livello di precisione (ad es.a causa di una dipendenza da hardware esterno) sei sulla piattaforma sbagliata e dovresti guardare un sistema operativo in tempo reale.

Altrimenti dovresti considerare se c'è un evento su cui puoi sincronizzarti o, nel caso peggiore, semplicemente occupato ad attendere la CPU e utilizzare l'API del contatore ad alte prestazioni per misurare il tempo trascorso.

Generalmente uno stato di sospensione durerà almeno fino a quando non si verifica la successiva interruzione del sistema.Tuttavia, ciò dipende dalle impostazioni delle risorse del timer multimediale.Può essere impostato su qualcosa di vicino a 1 ms, un po 'di hardware consente anche di funzionare in periodi di interruzione di 0,9765625 (Risoluzione effettiva fornito da NtQueryTimerResolution mostrerà 0,9766 ma in realtà è sbagliato.Semplicemente non riescono a inserire il numero corretto nel file Risoluzione effettiva formato.È 0,9765625 ms a 1024 interruzioni al secondo).

C'è un'eccezione che ci permette di sfuggire al fatto che potrebbe essere impossibile dormire per meno del periodo di interruzione:È il famoso Sleep(0).Questo è uno strumento molto potente e non viene utilizzato tutte le volte che dovrebbe!Rinuncia al promemoria dell'intervallo di tempo del thread.In questo modo il thread si fermerà finché lo scheduler non forzerà il thread a ottenere nuovamente il servizio della CPU. Sleep(0) è un servizio asincrono, la chiamata obbligherà lo scheduler a reagire indipendentemente da un'interruzione.

Un secondo modo è l'uso di a waitable object.Una funzione di attesa come WaitForSingleObject() può aspettare un evento.Per far sì che un thread rimanga inattivo per qualsiasi tempo, anche nel regime dei microsecondi, il thread deve impostare un thread di servizio che genererà un evento con il ritardo desiderato.Il thread "dormiente" imposterà questo thread e quindi si fermerà nella funzione di attesa finché il thread di servizio non imposterà l'evento segnalato.

In questo modo qualsiasi thread può "dormire" o attendere in qualsiasi momento.Il thread di servizio può essere di grande complessità e può offrire servizi a livello di sistema come eventi temporizzati con una risoluzione di microsecondi.Tuttavia, la risoluzione in microsecondi può forzare il thread del servizio a girare su un servizio temporale ad alta risoluzione per al massimo un periodo di interruzione (~ 1 ms).Se viene prestata attenzione, questo può funzionare molto bene, particolarmente su sistemi multi-processore o multi-core.Un giro di un ms non danneggia notevolmente il sistema multi-core, quando la maschera di affinità per il thread chiamante e il thread di servizio vengono gestite con attenzione.

Codice, descrizione e test possono essere visitati all'indirizzo Progetto timestamp di Windows

Ho lo stesso problema e nulla sembra essere più veloce di un ms, anche Sleep(0).Il mio problema è la comunicazione tra un client e un'applicazione server in cui utilizzo la funzione _InterlockedExchange per testare e impostare un bit, quindi dormo (0).

Ho davvero bisogno di eseguire migliaia di operazioni al secondo in questo modo e non funziona così velocemente come avevo pianificato.

Dato che ho un thin client che si occupa dell'utente, che a sua volta invoca un agente che poi parla con un thread, mi muoverò presto per unire il thread con l'agente in modo che non sia richiesta alcuna interfaccia di eventi.

Giusto per darvi un'idea di quanto sia lento questo sonno, ho eseguito un test per 10 secondi eseguendo un loop vuoto (ottenendo qualcosa come 18.000.000 di loop) mentre con l'evento in atto ho ottenuto solo 180.000 loop.Cioè, 100 volte più lento!

In realtà l'utilizzo di questa funzione usleep causerà una grande perdita di memoria/risorse.(a seconda della frequenza con cui viene chiamato)

usa questa versione corretta (scusa, non puoi modificare?)

bool usleep(unsigned long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec / 1000000ul;
    tv.tv_usec = usec % 1000000ul;
    bool success = (0 == select(0, 0, 0, &dummy, &tv));
    closesocket(s);
    return success;
}

Come tutti hanno menzionato, in effetti non ci sono garanzie sulla durata del sonno.Ma nessuno vuole ammettere che a volte, su un sistema inattivo, il comando usleep possa essere molto preciso.Soprattutto con un nocciolo senza solletico.Windows Vista ce l'ha e Linux ce l'ha dalla versione 2.6.16.

Esistono kernel Tickless per aiutare a migliorare la durata della batteria dei laptop:cfr.L'utilità powertop di Intel.

In quella condizione mi è capitato di misurare il comando usleep di Linux che rispettava molto fedelmente il tempo di sleep richiesto, fino a una mezza dozzina di microsecondi.

Quindi, forse l'OP vuole qualcosa che funzioni all'incirca per la maggior parte del tempo su un sistema inattivo e sia in grado di richiedere una pianificazione di micro secondi!In realtà lo vorrei anche su Windows.

Inoltre Sleep(0) suona come boost::thread::yield(), la cui terminologia è più chiara.

mi chiedo se Aumento-le serrature temporizzate hanno una precisione migliore.Perché allora potresti semplicemente bloccare un mutex che nessuno rilascia mai, e quando viene raggiunto il timeout, continuare...I timeout vengono impostati con boost::system_time + boost::milliseconds & cie (xtime è deprecato).

Prova a utilizzare ImpostaWaitableTimer...

Prova boost::xtime e timed_wait()

ha una precisione al nanosecondo.

Basta usare Sleep(0).0 è chiaramente inferiore a un millisecondo.Ora, sembra divertente, ma sono serio.Sleep(0) dice a Windows che non hai niente da fare in questo momento, ma che vuoi essere riconsiderato non appena lo scheduler verrà eseguito di nuovo.E poiché ovviamente non è possibile programmare l'esecuzione del thread prima dell'esecuzione dello scheduler stesso, questo è il ritardo più breve possibile.

Nota che puoi passare un numero di microsecondi al tuo usleep, ma lo stesso vale per void usleep(__int64 t) { Sleep(t/1000);} - nessuna garanzia di dormire effettivamente in quel periodo.

Funzione di sonno che dura molto meno di un millisecondo, forse

Ho scoperto che sleep(0) ha funzionato per me.Su un sistema con un carico vicino allo 0% sulla CPU nel task manager, ho scritto un semplice programma da console e la funzione sleep(0) ha dormito per 1-3 microsecondi costanti, ovvero molto meno di un millisecondo.

Ma dalle risposte sopra in questo thread, so che la quantità di ore di sonno sleep(0) può variare molto più selvaggiamente di questa su sistemi con un grande carico della CPU.

Ma a quanto ho capito, la funzione sleep non dovrebbe essere utilizzata come timer.Dovrebbe essere utilizzato per fare in modo che il programma utilizzi la minor percentuale possibile di CPU ed venga eseguito il più frequentemente possibile.Per i miei scopi, come spostare un proiettile sullo schermo in un videogioco molto più velocemente di un pixel al millisecondo, penso che sleep(0) funzioni.

Dovresti semplicemente assicurarti che l'intervallo di sonno sia molto inferiore al tempo massimo di sonno.Non usi lo sleep come timer ma solo per fare in modo che il gioco utilizzi la minima quantità possibile di percentuale di CPU.Dovresti utilizzare una funzione separata che non ha nulla a che fare con il sonno per sapere quando è trascorso un determinato periodo di tempo e quindi spostare il proiettile di un pixel sullo schermo, ad esempio 1/10 di millisecondo o 100 microsecondi. .

Lo pseudo-codice sarebbe più o meno questo.

while (timer1 < 100 microseconds) {
sleep(0);
}

if (timer2 >=100 microseconds) {
move projectile one pixel
}

//Rest of code in iteration here

So che la risposta potrebbe non funzionare per problemi o programmi avanzati, ma potrebbe funzionare per alcuni o molti programmi.

Se il tuo obiettivo è quello "attendere per un periodo di tempo molto breve" perché stai facendo un spinwait, quindi ci sono livelli crescenti di attesa che puoi eseguire.

void SpinOnce(ref Int32 spin)
{
   /*
      SpinOnce is called each time we need to wait. 
      But the action it takes depends on how many times we've been spinning:

      1..12 spins: spin 2..4096 cycles
      12..32: call SwitchToThread (allow another thread ready to go on time core to execute)
      over 32 spins: Sleep(0) (give up the remainder of our timeslice to any other thread ready to run, also allows APC and I/O callbacks)
   */
   spin += 1;

   if (spin > 32)
      Sleep(0); //give up the remainder of our timeslice
   else if (spin > 12)
      SwitchTothread(); //allow another thread on our CPU to have the remainder of our timeslice
   else
   {
      int loops = (1 << spin); //1..12 ==> 2..4096
      while (loops > 0)
         loops -= 1;
   }
}

Quindi, se il tuo obiettivo è effettivamente aspettare solo per un po', puoi usare qualcosa come:

int spin = 0;
while (!TryAcquireLock()) 
{ 
   SpinOne(ref spin);
}

La virtù qui è che aspettiamo più a lungo ogni volta, finendo per addormentarci completamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top