Zeitpunkt der Abarbeitung von Windowsnachrichten
-
Hallo zusammen
Ich habe mal eine eher allgemeine Frage zu Windows. Über Windowsnachrichten lassen sich ja verschiedene Events auslösen. Wenn ein Timer zuschlägt oder wenn ein Button gedrückt wird, werden Nachrichten versendet. Nun bin ich auf ein komisches Problem in meines Software gestoßen, bei der ich das Problem noch nicht ganz verstehe.
Da es mit den Windowsnachrichten zusammenhängt, wollte ich mal fragen, wann denn diese Nachrichten abgearbeitet werden. Bisher dachte ich, dass Windows diese bearbeiten, wenn gerade eh Leerlauf ist. Aber bei meinem Problem sieht es eher danach aus, dass die Abarbeitung zwischendurch angestoßen wird. Werden dann die aktuelle und die vom Event ausgelöste Methode quasi parallel abgearbeitet?
Wenn ich verstehe, wann und wie die Nachrichten abgearbeitet werden, sollte ich mein Problem lösen können. Daher postet mir auch gerne entsprechende Links.
-
Aus der MSDN zu SendMessage:
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed.
Zwischen Threads ist SendMessage also sozusagen ein priorisiertes PostMessage, das auf die Abarbeitung wartet.
-
Timer und Buttons werden nur ausgelöst, wenn auch die Nachrichtenschleife läuft.
Ansonsten werden Nachrichten mit SendMessage aofort ausgeliefert. Nachrichten mit PostMessage in die Queue gestellt und wieder aus der Nachrichtenschleife gezogen.
Bist Du also in der beliebigen Abarbeitung von Code in Deinem Thread, dann kommt Dir nichts dazwischen...
-
Danke für die Hinweise.
Es ist so, dass ich nur PostMessage benutze, um von einem erstellten Thread etwas an den Hauptthread zu senden.
Nun ist es so, dass über ein Event aus der GUI ein Pointer auf 0 gesetzt wird. Es kann aber passieren, dass mein anderer Thread eine Rückmeldung gibt, die eine Aktion auf diesen Pointer auslöst. In der Methode, die durch die Nachricht des Threads ausgelöst wird, sieht das in etwa so aus:
if(meinPointer != 0) { meinPointer->doSomething(); } else { doDebugMessage(); }Allerdings kommt (aber sehr selten) in der Zeile "meinPointer->doSomething();" die Meldung, dass der Pointer ungültig ist oder auf einen falschen Speicherbereich zeigt (den genauen Text weiss ich gerade nicht).
Nach eurer Erklärung würde die Methode, die durch die Threadnachricht ausgelöst wird, erst angestoßen, wenn das vorherige Event und dessen Methode vollständig abgearbeitet ist. Somit dürfte mein oben beschriebener Fehler gar nicht auftreten.
Habt ihr dazu vielleicht auch noch eine Idee?
-
Zeiger per PostMessage weiterzugeben ist eine Sache für sich.
Wer gibt denn den Speicher wieder frei?
Garantiert Deine Methodik, dass der Zeiger wirklich gültig ist, egal wann die Nachricht vom Thread mit der Message Queue abgearbeitet wird?Man sollte keine Zeiger so mit PostMessage übergeben. Man sollte nur Benachrichtigungen damit auslösen. Die Daten werden am Besten in einem gemeinsamen Container gehalten, den man mit einer CriticalSection absichert.
-
Da habe ich mich wohl nicht ganz klar ausgedrückt. Der Pointer wird nicht über die Nachricht versendet. Bei beiden Events bin ich im gleichen Objekt. Nur die Events werden einmal von der GUI und beim anderen Mal von nem Thread ausgelöst.
Wird das GUI-Event ausgelöst, werden verschiedene Objekte aufgerufen, wobei der Pointer in Objekt A auf 0 gesetzt wird. Wird das Thread-Event ausgelöst, wird in Objekt A kontrolliert, ob der Pointer != 0 ist. Ist das der Fall, wird darin eine Methode ausgelöst.
Der Pointer ist eine Membervariable von Objekt A. Nun ist es aber so, dass manchmal der Pointer eine EAccessViolation auslöst. Dabei wird das Objekt hinter dem Pointer erst gelöscht, nachdem der Pointer in Objekt A auf 0 gesetzt wurde.
Und daher verstehe ich nicht, wie der Fehler auftreten kann.
-
Wan erneuerst Du den zeiger?
Wer gibt ihn frei?
Kann es sein, das Thread 1 den Speicher freigibt während Thread 2 auf diesen Speicher zugreifen will?
-
Entschuldige, dass ich mich erst jetzt melde.
Also, das Objekt wird zu Beginn eines Anwendungsfall erzeugt. Es meldet sich dann bei einem anderen Objekt an, welches Aktionen, die über Events gestartet werden sollen, bei angemeldeten Objekten auslöst (z.B. eine Fortschrittsmeldung). Wenn der Anwendungsfall abgebrochen wird, meldet sich Objekt A ab (der Zeiger dort wird auf 0 gesetzt). Danach wird das Objekt gelöscht.
Es wird immer nur im Hauptthread auf das Objekt zugegriffen. Allerdings gibt es zwei Möglichkeiten entsprechende Aktionen auszulösen. Entweder über ein Event aus der GUI oder durch eine Windowsnachricht aus einem anderen Thread. Diese Windowsnachricht beinhaltet aber keine Daten. Sie hat nur einen bestimmten Typ, um damit die Aktion anzustoßen.
Ich hoffe, es wird dadurch klarer, wie meine Software aufgebaut ist. In meinen Augen dürfte das Problem nicht auftreten, aber vielleicht habt ihr ja noch eine Idee bzw einen Ansatz.
-
Wenn von dem Thread kein Zugriff auf das Objekt erfolgt, sehe ich kein Problem.
Woher weiß der Mainthread für wenn eine Nachricht ist, wenn keine Daten mitgegeben werden?
-
Es wird, glaube ich, zu umfangreich, hier zu erklären, was wann wie wo abläuft. Anscheinend werden ja alle Windowsnachrichten nacheinander abgearbeitet. Wenn durch eine Nachricht keine Unterbrechung der aktuellen Aktion auftreten kann, dürfte das Problem nicht auftreten.
Daher vermute ich, dass das Problem doch durch etwas anderes ausgelöst wird. Da es nur sporadisch auftritt, werde ich das wohl mal genauer untersuchen müssen. Als wichtigen Anhaltspunkt nehme ich aber mit, dass es kein Synchronisationsproblem sein kann, wenn alles im Hauptthread abläuft und nur über Nachrichten angestoßen wird.
-
Das Problem kann alleine schon sein, dass Du das Objekt abräumst oder veränderst und nachträglich noch eine nachricht eintrudelt die etwas damit macht.
Hilfreich ist sicherlich mal den Callstack anzusehen wer die Aktion auslöst, die auf das ungültige Objekt zugreift.