2012-01-14 14 views
14

In un'applicazione di test del client di messaggistica, il thread del produttore deve essere limitato per evitare l'inondazione del server.Sospendere un thread per meno di un millisecondo

Poiché le velocità di trasferimento sono di circa 25.000 messaggi al secondo (40 microsecondi per messaggio), il ritardo causato da Sleep (1) sarebbe troppo lungo.

How to make thread sleep less than a millisecond on Windows contiene alcune informazioni relative all'API di Windows. C'è uno snippet di codice, una classe o una libreria per Delphi?


seguente risposta Bens ho scoperto che il sonno con diversi valori sotto 15 fornisce anche tassi differenti di trasferimento (Windows Vista):

sonno (1), dopo ogni 20 messaggi:

00:02 tx/rx 25740/3705 12831/1846 msgs/sec (77/541 microsecs/msg) 
00:04 tx/rx 53101/7405 13255/1848 msgs/sec (75/541 microsecs/msg) 
00:06 tx/rx 79640/11288 13260/1879 msgs/sec (75/532 microsecs/msg) 
00:08 tx/rx 104520/14562 13055/1818 msgs/sec (76/550 microsecs/msg) 
00:10 tx/rx 130760/18829 13066/1881 msgs/sec (76/531 microsecs/msg) 

sonno (5) dopo ogni 20 messaggi:

00:02 tx/rx 7640/3622 3812/1807 msgs/sec (262/553 microsecs/msg) 
00:04 tx/rx 14660/10794 3661/2695 msgs/sec (273/371 microsecs/msg) 
00:06 tx/rx 21480/18171 3577/3026 msgs/sec (279/330 microsecs/msg) 
00:08 tx/rx 28140/25642 3515/3203 msgs/sec (284/312 microsecs/msg) 
00:10 tx/rx 34980/32692 3496/3267 msgs/sec (286/306 microsecs/msg) 

Questo è stato inaspettato dopo aver letto il commento ab il limite inferiore per occupato in attesa

E i valori senza strozzare

00:02 tx/rx 44065/494 21988/246 msgs/sec (45/4065 microsecs/msg) 
00:04 tx/rx 90493/756 22595/188 msgs/sec (44/5319 microsecs/msg) 
00:06 tx/rx 142982/907 23810/151 msgs/sec (41/6622 microsecs/msg) 
00:08 tx/rx 192562/1144 24055/142 msgs/sec (41/7042 microsecs/msg) 
00:10 tx/rx 237294/1395 23717/139 msgs/sec (42/7194 microsecs/msg) 
+0

è possibile implementare solo occupato in attesa per meno di 1 ms, not Sleep. – kludg

+0

@Serg, AFAIK circa 15 ms – OnTheFly

risposta

21

Invia 20 messaggi e poi dormire per 1 millisecondo?

Non si può dormire per meno del quantum dello scheduler, a meno che non si abbia un interrupt hardware diverso dal timer di sistema. Leggi le risposte nella domanda che hai collegato, spiegano perché l'approccio suggerito in realtà non funziona.

Anche con questo approccio si potrebbe dormire più di 1 ms, o poiché i messaggi non vengono inviati istantaneamente, l'intera operazione richiederà sicuramente più di 1 ms, riducendo la frequenza complessiva.

Quindi, ispezionare una sorgente di clock di precisione ogni volta che ti svegli, e calcolare quanti messaggi da inviare in base al tempo trascorso, non utilizzare una costante 20.

+3

Su Windows, consiglio QueryPerformanceCounter (http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx) come precisione sorgente di clock (fino a una risoluzione di circa mezzo microsecondo). – Crashworks

+0

+1, sicuramente l'approccio migliore: elimina il bisogno del breve ritardo. –

12

1) Ottenere l'ora corrente.

2) Calcolare il numero di messaggi che è necessario inviare in base al numero di messaggi inviati e al tempo trascorso da quel momento.

3) Inviare tanti messaggi.

4) Dormire il più piccolo possibile.

5) Passare al punto 1.

0

Invece di dormire, perché non utilizzare TTheard.Yield lasciare un altro thread/processo avere un colpo al processore?

+0

.. potrebbero non esserci altri thread pronti, nel qual caso Yield non fa nulla. –

3

Come altri hanno già detto, non è possibile dormire per un breve periodo di tempo (e anche Sleep (1) non è affidabile, potrebbe facilmente dormire per più di 1 ms).

La soluzione migliore è calcolare l'ora per il messaggio successivo e quindi eseguire un ciclo di attesa occupato e controllare l'ora, fino a quando non si verifica il tempo desiderato. Di seguito è una soluzione completa insieme a una piccola struttura di test.

program SendEquidistantMessages; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    Windows, SysUtils; 

procedure SendMessage(msgNum: integer); 
begin 
    // send the message here 
end; 

procedure WaitUntil(nextMsgTime: int64); 
var 
    currTime: int64; 
begin 
    repeat 
    QueryPerformanceCounter(currTime); 
    if currTime >= nextMsgTime then 
     break; //repeat 
    asm pause; end; 
    until false; 
end; 

procedure SendMessages(numMsg, msgPerSec: integer); 
var 
    iMsg  : integer; 
    nextMsgTime: int64; 
    perfFreq : int64; 
    prevMsg : int64; 
    startTime : int64; 

begin 
    Assert(QueryPerformanceFrequency(perfFreq)); 
    Assert(QueryPerformanceCounter(startTime)); 
    for iMsg := 1 to numMsg do begin 
    WaitUntil(Round(startTime + iMsg/msgPerSec * perfFreq)); 
    SendMessage(iMsg); 
    end; 
end; 

var 
    time: cardinal; 

begin 
    try 
    time := GetTickCount; 
    SendMessages(20000, 5000); 
    time := GetTickCount-time; 
    Writeln('20.000 messages sent in ', time/1000:4:1, ' sec; ', 
     'required rate = 5000 msg/sec, real rate = ', 20000/(time/1000):6:1, ' msg/sec'); 
    Readln; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 
+1

Ove applicabile (con la versione recente di Delphi), sostengo l'uso di 'Diagnostic.TStopWatch' per monitorare il tempo trascorso: consente l'accesso ai timer ad alta risoluzione, se disponibili. – menjaraz

+0

TStopWatch è anche un bene per la compatibilità OS/X - buon punto! – gabr

0

C'è un filo Sleep Less Than One Millisecond che fare con problemi simili. Ho dato alcuni dettagli su come lasciare che un thread dorma per sempre. Questo ovviamente include anche i posti letto nella gamma dei microsecondi. Argomenti: eventi temporizzati creati da thread di servizio, funzioni

Maggiori dettagli possono essere trovati anche a Windows Timestamp Project

0

una soluzione è quella di ottenere il tempo corrente con nano secondi e inoltre con il tempo che si desidera dormire Wait (attesa) e ciclo fino a quando l'ora corrente (con nano secondi) diventa superiore al tempo avete calcolato, v'è un metodo che illustrano questo:

public void wait(long nano){ 
    long t= System.nanoTime(); 
    t+= nano; 
    while(System.nanoTime()<t); 
} 

noti che: 1 secondo = 1000 millisecondi = 1000000 microsecondi = 1000000000 nanosecondi

così

1 millisecondo = 1000000 nanosecondi

Problemi correlati