Entwickler-Ecke

Windows API - Sleep 1msec in schleife problem (nicht immer)


fuba - So 01.08.10 12:24
Titel: Sleep 1msec in schleife problem (nicht immer)
hi leute,

habe mir eine procedure gebaut, welche mir ermöglichen soll, ein "sleep" zu unterbrechen bzw es werden während des Sleep 2 Labels Updated.

meien procedure:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
procedure MySleep(aMSecDelay: integer);
var
  i, c: integer;
  Event: THandle;
begin
  c:=0;
  Event := CreateEvent(nil, False, False, nil);
  RemCount:=aMSecDelay;
  From1.Label2.Caption:=SecToDMS(RemCount div 1000);
  for i:=1 to aMSecDelay do
  begin
    Application.ProcessMessages;
    if DoAbort then break; // DoAbort = Globale Variable
    Dec(RemCount);
    Inc(TimeCount);
    Inc(c);
    WaitForSingleObject(Event, 1);
    //Sleep(1); // auch schon mit Sleep versucht
    if (c > 999then // Update nur jede sekunde
    begin
      Form1.Label1.Caption:=SecToDMS(TimeCount div 1000);
      From1.Label2.Caption:=SecToDMS(RemCount div 1000);
      c:=0;
    end;
  end;
end;

wie ihr sehr, habe ich es schon mit sleep und WaitForSingleObject versucht, jedoch bei beiden das gleiche problem.
Das komische ist, manchmal (nicht immer) werden die Labels 1x Pro sekunde Updated (wie gewollt) und dann wieder 1x alle 3-5 sekunden.

Was mache ich falsch?


Hidden - So 01.08.10 13:09

Hi :)

Offensichtlich, macht Windows im Hintergrund noch andere Sachen. Auf jeden Fall kann ich dir aber sagen, dass zumindest Sleep() - so wie auch ein Timer - keine Zeitintervalle unter 50ms schafft, das hängt bereits auf Betriebssystem-Ebene. Die Wartezeit sollte also meines Wissens zumindest mit Sleep nie stimmen. :gruebel:

Wichtig zu wissen ist, dass Sleep() die Recourcen trotzdem verbraucht, sie also auch anderen Anwendungen nicht zur Verfügung stehen :!: (Stichwort "busy waiting"). Deshalb verwendet man meißtens einen TTimer bzw. die der TTimer-Klasse zu grunde liegende WinMessage, und Sleep() eher mal aus Bequemlichkeit.

lg,


Narses - So 01.08.10 13:23

Moin!

Kann sein, du suchst sowas hier [http://www.delphipraxis.net/post52152.html#52152]? :idea: :nixweiss:

cu
Narses


Delete - So 01.08.10 13:44

user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
Auf jeden Fall kann ich dir aber sagen, dass zumindest Sleep() - so wie auch ein Timer - keine Zeitintervalle unter 50ms schafft,

Das stimmt nicht. Ein Sleep(n) führt dazu dass der Thread den Rest der verbleibenden Zeitscheibe an das System abgibt, wenn n kleiner ist als die Länge einer Zeitscheibe. Eine Zeitscheibe ist unter Windows ca. 22. Millisekunden lang. Das heißt für 22 Sekunden bekommt der Thread Rechenzeit auf der CPU zugeteilt. Ist n kleiner als 22 Millisekunden gibt der Thread den Rest seiner Zeitscheibe an das System ab.

Zitat:
Wichtig zu wissen ist, dass Sleep() die Recourcen trotzdem verbraucht, sie also auch anderen Anwendungen nicht zur Verfügung stehen

Genau das tut Sleep nicht. Wie schon oben erwähnt gibt es den Rest der verbleiben Zeitscheibe ab und verbraucht somit auch keinerlei Ressourcen.

Die 50 Millisekunden Untergrenze gilt nur für Timer-Nachrichten und auch nur unter Windows 95, 98 und Millenium. Unter NT und höher beträgt der Wert ca. 15 Millisekunden oder so.

Und während Sleep Code ausführen zu wollen kann nicht funktionieren, da der Thread seine Rechenzeit abgibt und somit keinen Code ausführen kann. Der Thread kann erst wieder Code ausführen, wenn er wieder im zuteilungsfähigen Zustand versetzt wird nach dem Sleep und dann wieder an der Reihe ist und Rechenzeit zugeteilt bekommt.


Xion - So 01.08.10 14:10

Was du machen kannst ist, du verwendest einen Timer und guckst per GetTickCount wieviel Zeit denn tatsächlich vergangen ist. Das funktioniert natürlich nur für größere Intervalle, so dass die "Quantelung"(15ms wie oben geschrieben) der Timer-Events nicht ins Gewicht fällt (also vielleicht so ab 150ms).

Vielleicht gäbs da auch noch ne Möglichkeit mit OnIdle *in die Runde werf*


fuba - So 01.08.10 14:34

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Moin!

Kann sein, du suchst sowas hier [http://www.delphipraxis.net/post52152.html#52152]? :idea: :nixweiss:

cu
Narses


hab es damit auch schon versucht, aber bei 1msec kommt das gleiche raus wie bei sleep.
(manchmal gehts, manchmal nicht)

user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:

Und während Sleep Code ausführen zu wollen kann nicht funktionieren, da der Thread seine Rechenzeit abgibt und somit keinen Code ausführen kann. Der Thread kann erst wieder Code ausführen, wenn er wieder im zuteilungsfähigen Zustand versetzt wird nach dem Sleep und dann wieder an der Reihe ist und Rechenzeit zugeteilt bekommt.


jop, deswegen wollte ich msecDelay auch in ner schleife mit sleep(1) durchlaufen.
nur wusste ich nicht, dass an sleep nicht so niedrige werte übergeben werden sollen :D

wenn ich also bei meinem MySleep nur sekunden übergebe, also msec div 1000, würde es also klappen?

also so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure MySleep(aSecDelay: integer);
var
  i: integer;
  Event: THandle;
  RemCount: integer;
begin
  Event := CreateEvent(nil, False, False, nil);
  RemCount:=aSecDelay;
  From1.Label2.Caption:=SecToDMS(RemCount);
  for i:=1 to aSecDelay do
  begin
    Application.ProcessMessages;
    if DoAbort then break; // DoAbort = Globale Variable
    Dec(RemCount);
    Inc(TimeCount); // TimeCount wird bei FormCreate auf 0 gesetzt, soll also gesamtzeit übergeben
    WaitForSingleObject(Event, 1000);

    // Update der Labels
    Form1.Label1.Caption:=SecToDMS(TimeCount);
    From1.Label2.Caption:=SecToDMS(RemCount);
  end;
end;


Falls ich das richtig verstanden habe, solle man an sleep bei NT Systemen keinen kleineren wert als ~30msec angeben, richtig?


Delete - So 01.08.10 15:25

Mit folgendem Code kannst Du herausfinden, was Dein BS (WIN XP, WIN732, WIN764) auf der vorhandenen Hardware (Solo,Duo) bei Sleep() an Zeit "verbraucht":


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TForm1.Button1Click(Sender: TObject);
var a, b, c: Int64; r: real; i: Integer;
begin
For i:=0 to 20 do
BEGIN
  QueryPerformanceFrequency(a);
  QueryPerformanceCounter(b);
  Sleep(i);
  QueryPerformanceCounter(c);
  r:= (c - b) * 1000 / a;
  Memo1.lines.add('Sleep('+IntToStr(i)+') = '+ FloatToStrF(r, ffFixed, 125)+' msec');
  Application.ProcessMessages;
END;
end;


fuba - Mo 02.08.10 03:41

Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

werd ich mir mal beiseite legen,danke :D

ach ja, wie es aussieht funktioniert die übergabe von sekunden, frage also beantwortet.
Danke an alle :)