Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Warten auf ...? MsgWaitForMultipleObjects (https://www.delphipraxis.net/6853-warten-auf-msgwaitformultipleobjects.html)

Gast 24. Jul 2003 21:32


Warten auf ...? MsgWaitForMultipleObjects
 
Kann mir jemand sagen, wie ich es einfach OHNE Angabe von Objekten hinbekomme eine Message-Queue aufzubauen? Ich will, daß der Thread nachdem eine Message gepostet wurde, bzw nachdem ein Timeout auftrat jeweils etwas macht. Wie geht das?

Wenn ich MsgWaitForMultipleObjects(0, PDWORD(nil) ...) mache, dann wartet er nur EINMAL. Ich brauche es aber in einer Schleife. Will heißen, daß dieses "Nullobjekt" danach scheinbar signalisiert ist :-/

Bitte um Antworten.

jbg 24. Jul 2003 23:43

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Warum nutzt du nicht einfach GetMessage oder PeekMessage im Thread? Windows erzeugt dann automatisch eine Message-Queue für den Thread.

Gast 24. Jul 2003 23:51

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Das ist mir bewußt. So mache ich es notgedrungen auch schon.

Aber ich will
1. Warten auf eine Message (die dem Thread sagt sich zu beenden)
2. In Intervallen die Rechner im Netzwerk listen.

Verstehst du jetzt das Problem? Klar kann ich pollen und dann warten. So kann ich zumindest 99%ige Auslastung verhindern. Allerdings verlängert dies die Zeit, die der Thread beim Schliessen bleibt. Deshalb würde ich die Methode gern mit o.g. Funktion erweitern.

negaH 25. Jul 2003 00:17

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Ohne Object ? ich nutze einfach ein Event := CreateEvent(); also Object.

Allerdings brauchst du dann auch nicht mehr mit Messages zu arbeiten, sondern kannst gleich im Thread auf ein TerminierungsEvent warten.
Der Weg über MsgWait...() ist schon richtig, im Gegensatz zu GetMessage() und PeekMessage(). Er reduziert die CPU Last erheblich da nun von Polling auf Eventorierntiert umgestellt wird.

Ähm, vielleicht geht ja MsgWaitForMultipleObjects(1, GetCurrentThread, ..); ?? Ich habs noch nicht probiert, aber so lange der Thread auf seine eigene Terminierung wartet, wartet er eigentlich vergeblich !!

Gruß hagen

negaH 25. Jul 2003 00:24

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Scheint zu gehen :)

Delphi-Quellcode:
procedure Delay(Milliseconds: Integer);
var
  Tick: DWord;
  Event: THandle;
begin
  Event := GetCurrentThread;
  Tick := GetTickCount + DWord(Milliseconds);
  while Milliseconds > 0 do
    case MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) of
      WAIT_TIMEOUT: Exit;
      WAIT_OBJECT_0: WriteLn('Fails');
    else
      begin
        Application.ProcessMessages;
        Milliseconds := Tick - GetTickcount;
      end;
    end;
end;
Gruß Hagen

Luckie 25. Jul 2003 01:25

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Mal ne blöde Frage. Der Übergabe parameter ist vom Typ Integer. Und wenn du ihn in der Funktion benutzt castest du ihn nach DWORD. warum übergibst du nicht gleich ein DWORD? Macht auch mehr Sinn oder wie wartet man minus 7 Sekunden?

negaH 25. Jul 2003 02:36

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Delphi-Quellcode:
if DWord(-7) >= 0 then
  while (Dword(Milliseconds) > 0) or TRUE do ;

if (Milliseonds in [-MaxInt..+MaxInt]) and
   (GetTickCount in [0..MaxCardinal]) and
    SourceCompiledWithDelphiAbove5 then
  Writeln('WARNING: unterschiedliche Typen, beide Typen werden erweitert und Laufzeitfehler bringt Bereichsfehler');
Anders gesagt: wäre Milliseconds Type DWord dann könnte die letzte Zeile Milliseconds := Tick - GetTickCount, Milliseonds mit Wert < 0 errechnen. Somit würde die while Milliseconds > 0 do Schleife nur terminieren wenn Milliseconds exakt ZERO ist. Also muß Milliseconds Type Integer sein. Der TypCast nach DWord() wiederum soll die Compilerüberprüfung deaktivieren, sowohl den Compiler mit seiner Warnung der unterschiedlichen Integertypen die kombiniert werden, wie auch die Laufzeitfehler-überprüfung die wenn aktiviert einen Bereichfehler melden würde. Beide Überprüfungen SIND in diesem Falle schwachsinnig da selbst wenn Milliseconds Type Int64 wäre, 1. kein Mensch so lange Delays macht und 2. GetTickCount eh bei 2^32-1 überlaufen würde.
Andererseits müsste man den Code so umschreiben das er entweder eine zusätzliche lokale Variable nutz um korrekt umzurechnen, oder aber man musst in der while Schleife zusätzlich GetTickCount aufrufen. Nämlich

Delphi-Quellcode:

while Tick > GetTickCount do
  case MsgWait..(.., Tick - GetTickCount) of
Der Cast sichert also den Code ab, optimiert eine lokale Var weg bzw. einen unnötigen Aufruf von GetTickCount;


Also alles ok mit dem Source :) Gruß Hagen

Gast 25. Jul 2003 08:50

Re: Warten auf ...? MsgWaitForMultipleObjects
 
@Hagen: Vielleicht klingt es nicht nur trivial, sondern ist es auch. Aber ich bekomme es nicht auf die Reihe. Vielleicht nur ein Schusselfehler?!

Es ist nicht möglich auf das Handle von GetCurrentThread() zu warten. Das habe ich auch schon gedacht gehabt (hätte ich auch dazuschreiben können ;)) ... das gibt WAIT_FAILED.
Auch mit CreateEvent() hatte ich es zum Zeitpunkt des Posts schon probiert. Doch vergeblich die Liebesmüh.

Wenn ich mit MsgWaitForMultipleObjects() auf ein echtes Objekt, oder mit der Anzahl 0 (und dereferenziertem NIL als zweitem Parameter) arbeite, dann wartet er einmal ... danach rasselt er nur noch durch. Selbst ein Manual-Reset Event, welches ich innerhalb der Schleife zurücksetzte hat nix gebracht. Also das eigentliche Problem hier lautet wirklich nicht "wie benutze ich eine Win32 API?" vielmehr geht es darum, wie ich die o.g. API zum laufen bekomme. Habe mal gegooglet und wie es scheint, gab es noch andere Leute mit meinem Problem.

Also von der Reihenfolger her sollten die Aufrufe etwa so aussehen

Code:
while true do
begin
  if MsgWaitForMultipleObjects(0, PDWORD(nil)^, True, 5000, QS_ALLEVENTS)<>WAIT_FAILED then
  if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
  case msg.Message of
  WM_QUIT: ...;
  end
  else
  begin
//    Eigentlicher Job ...
  end;
end;

jbg 25. Jul 2003 08:50

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Zitat:

Zitat von negaH
im Gegensatz zu GetMessage() und PeekMessage(). Er reduziert die CPU Last erheblich da nun von Polling auf Eventorierntiert umgestellt wird.

Bei PeekMessage stimme ich dir zu, aber GetMessage betreibt kein Polling.

Gast 25. Jul 2003 09:53

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Um es noch mal zusammenzufassen: Ich brauche eine Funktion wie WaitMessage ... aber mit Timeout!!!

@jbg: Stimmt, PeekMessage kehrt sofort zurück ... egal ob ne Message da war oder nicht!

negaH 25. Jul 2003 12:40

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Du musst den kompletten Messagequeue nach einem Signal leeren, ansonsten könnte es vorkommen das die eine Message die du mit Peekmessage entfernst eine neue Message postet. In diesem Moment muß MsgWaitForSingleObject() immer wieder sofort zurückkehren, da ja eine Message im Queue ist.

Delphi-Quellcode:

procedure Test;
var
  Msg: TMsg;
begin
  WriteLn('Start');

  while True do
    case MsgWaitForMultipleObjects(0, PDWord(nil)^, False, 100, QS_ALLEVENTS) of
      WAIT_OBJECT_0:
        if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
//        while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
          case Msg.Message of
            WM_QUIT: WriteLn('Quit');
          else
            begin
              WriteLn('Message');
              TranslateMessage(Msg);
              DispatchMessage(Msg);
            end;
          end;
      WAIT_FAILED: WriteLn('Failed');
      WAIT_TIMEOUT: WriteLn('Timeout');
    end;

  WriteLn('End');
end;
Obiger code funktioniert bei mir. Stimmt, mit dem Threadhandle als Event gibt er WAIT_FAILED zurück. Damit ist mein vorletztes Posting falsch.

Bei mir gibt obiger Source in meiner Console aus:

Start
Message
Message
Timeout
Timeout
Message -> bewegte Mousecursor
Timeout
Timeout
Timeout
Message -> Idle message wm_Timer

usw. usw.

Wenn der Timeout zu groß ist dann kann dieser nicht mehr eintreten da in normalen Applications das OS weakup Messages versendet, zB. wm_SysTimer.



Gruß Hagen

negaH 25. Jul 2003 12:53

Re: Warten auf ...? MsgWaitForMultipleObjects
 
@jbg

Zitat:

Bei PeekMessage stimme ich dir zu, aber GetMessage betreibt kein Polling
Tja ich weiß nicht so recht. Woher hast du diese Informationen ??

Ich gehe mal von der Logik aus.
PeekMessage() schaut nur im Queue nach ob eine Message vorhanden ist und liefert diese zurück. Damit könnte das OS also ein Flag pro Thread verwalten (zB. Zähler) um anzuzeigen das Messages im Queue sind. PeekMessage() in einer Loop würde also die meiste Zeit dieses Flag pollen, sofort zurückkehren und dann wiederum sofort Pollen.

Bei GetMessage() hängt das von der Implementation ab. Da ich die nicht kenne muß ich Annahmen treffen.
Der günstigste Fall nimmt an das GetMessage() so wie oben arbeitet. Also Ereignissorientiert, schön.
Der ungünstige Fall nimmt an das GetMessage() mehr erledigen muß als nur Messages zu holen.
Das GetMessage() noch aus Win2.0 bekannt ist und demzufolge Kompatibilitätsrestriktionen unterworfen ist.
Das GetMessage() als Highlevel Funktion auf LowerLevel API zurückgreift und einfach in einer Loop per PeekMessage() pollt.

Es steht also 3 zu 1 das GetMessage() pollen KÖNNTE. Also nehme ich an das der schlechteste Fall der ist mit dem ich rechnen muß, also Polling. Nutze ich obigen Weg so stelle ich garantiert auf Ereignissorierntiert um egal ob underlaying GetMessage() pollt oder auf Events aufsetzt. Ich egalisiere den Fakt ob gepollt wird oder nicht.

Gruß Hagen

Gast 25. Jul 2003 12:55

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Danke Hagen.

Dein obiger Post war nicht wirklich falsch. Du hattest es ja nur vorgeschlagen :)

Aber du meinst, wenn ich sie dispatche dann klappt es - das muss ich mal probieren. TranslateMessage() muesste ich ja weglassen koennen, weil ich ja eh keinen Dialog an der Queue zu haengen habe. Ich teste aber vorsichtshalber beides.

Danke vorerst. Falls sich noch was ergibt, melde ich mich nochmal. Ich hatte auch schon an eine Message gedacht ... vonwegen PeekMessage wartet nicht, bis eine gesendete Message auch gepostet wurde und so ... aber ich ging davon aus, dass wenn ich sie nicht sende, wohl kaum "irgendjemand" anders das tut.

negaH 25. Jul 2003 13:03

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Hi Asserbad,

wenn deine Funktion fertig ist würde ich sie gerne sehen. Normalerweise sendet das OS periodisch Messages. Viele Windows Klassen nutzen einen SysTimer() der dann alle 500ms eine wm_SysTimer Message auslösst.

Setze ich Timeout auf 5000 dann kommt nie WAIT_TIMEOUT da in der Zwischenzeit wm_SysTimer o.ä. periodisch gepostet wurde.

Gruß Hagen

Gast 25. Jul 2003 22:58

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Wie gewuenscht, mein Code:

Delphi-Quellcode:
function MAC_Thread(pml: PMachineList): DWORD; stdcall;
var
  msg: TMsg;
begin
  while True do
    case MsgWaitForMultipleObjects(0, PDWORD(nil)^, False, 5000, QS_ALLEVENTS) of
      WAIT_OBJECT_0:
        if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
          case msg.message of
            SIGNAL_STOP:
              begin
{$IFDEF DEBUGGING}
                Writeln('MAC: Received STOP.');
{$ENDIF DEBUGGING}
                Exit;
              end;
          else
            DispatchMessage(msg);
          end;
      WAIT_TIMEOUT:
        begin
{$IFDEF DEBUGGING}
          Writeln('MAC: Timeout ... scanning for machines');
{$ENDIF DEBUGGING}
          PostThreadMessage(pml^.Dispatcher, SIGNAL_MACLISTUPD, 0, 0);
        end;
    end;
  result := 0;
end;
Es war tatsaechlich die Sache mit dem Entfernen aus der Queue. Uebrigens, ich bekomme bei 5sec einen Timeout!

Hier noch ein Workerthread:

Delphi-Quellcode:
function WorkerThread(x: Pointer): DWORD; stdcall;
var
  msg: TMsg;
begin
  while True do begin
    if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
      case msg.message of
        SIGNAL_STOP:
          begin
{$IFDEF DEBUGGING}
            Writeln('WRK: Received STOP.');
{$ENDIF DEBUGGING}
            Exit;
          end;
        SIGNAL_WRKSCANMAC:
          begin
{$IFDEF DEBUGGING}
            Writeln('WRK: Now scanning machine with index ', msg.wParam);
{$ENDIF DEBUGGING}
            DispatchMessage(msg);
          end;
      else
        DispatchMessage(msg);
      end;
    WaitMessage; // lay down to sleep ... until we receive a message
  end;
  result := 0;
end;

negaH 26. Jul 2003 00:59

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Zitat:

Es war tatsaechlich die Sache mit dem Entfernen aus der Queue. Uebrigens, ich bekomme bei 5sec einen Timeout!

Korrekt da es eine Theadfunktion ist und dort kein bescheuerte Windows-Klasse die wm_SysTimer=$0118 Message erwartet. So bald aber ein blinkender Cursor im Spiel ist wird mit Sicherheit ein wm_SysTimer ausgelösst.

Eine Frage: Du nutzt den Weg über PostMessages() an Threads um eine zügige Abarbeitung zu gewährleisten ?? Ich frage weil du damit Kernel und User Objecte mixt, obwohl es doch besser wäre bei Threads mit reinen Kernel zu arbeiten, sprich Events, Semaphore usw.


Gruß Hagen

jbg 26. Jul 2003 01:31

Re: Warten auf ...? MsgWaitForMultipleObjects
 
Zitat:

Zitat von negaH
@jbg

Zitat:

Bei PeekMessage stimme ich dir zu, aber GetMessage betreibt kein Polling
Tja ich weiß nicht so recht. Woher hast du diese Informationen ??

Debugging.

GetMessage ruft die gleiche interne Funktion auf, die auch WaitMessage aufruft. Und zu WaitMessage sagt das PSDK
Zitat:

The WaitMessage function yields control to other threads when a thread has no other messages in its message queue. The WaitMessage function suspends the thread and does not return until a new message is placed in the thread's message queue.
Sieht mir doch stark nach "nicht polling" aus.

Zitat:

PeekMessage() in einer Loop würde also die meiste Zeit dieses Flag pollen, sofort zurückkehren und dann wiederum sofort Pollen.
Das merkt man an der CPU Belastung. (Zusatzinfo: Application.ProcessMessages ruft auch nur PeekMessage in einer Schleife auf)

Zitat:

Der günstigste Fall nimmt an das GetMessage() so wie oben arbeitet. Also Ereignissorientiert, schön.
Es sieht danach aus und "fühlt" sich auch so an.

Zitat:

Der ungünstige Fall nimmt an das GetMessage() mehr erledigen muß als nur Messages zu holen.
Ja. Es macht mehr und zwar vor dem internen WaitMessage und danach. Was es da nun genau macht, habe ich mir nicht angeschaut. Es dürfte aber mit den Parametern und dem Auslesen der Message-Queue zu tun haben.

Zitat:

Das GetMessage() noch aus Win2.0 bekannt ist und demzufolge Kompatibilitätsrestriktionen unterworfen ist.
Zugegeben, damals gab es noch keine Threads, aber es wurde trotzdem Rechenzeit an andere Anwendungen abgegeben. Nach außen hat sich nichts geändert. Die Messageloop sieht auch immer noch so aus, wie sie zu Win 3.1 Zeiten war.

Zitat:

Das GetMessage() als Highlevel Funktion auf LowerLevel API zurückgreift und einfach in einer Loop per PeekMessage() pollt.
Das macht sie auf keinen Fall. Das ist eher das, was Application.ProcessMessages macht.

Zitat:

Es steht also 3 zu 1 das GetMessage() pollen KÖNNTE.
Habe ich das jetzt mit obigen Argumenten wiederlegt?

negaH 26. Jul 2003 01:39

Re: Warten auf ...? MsgWaitForMultipleObjects
 
@jbg, ja haste, danke für die Infos.

Was ich eigentlich zeigen wollte ist der Punkt wie ich programmiere wenn ich nur Vermutungen anstellen kann (einfach keine Zeit oder Lust alles debuggen zu müssen :-)

Das Resultat dieser Überlegungen führt dazu das es, egal in welchem Context, nicht mehr pollt, alles bestens also und auch gut.

Gruß Hagen


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz