AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Events in TThread

Ein Thema von DocE · begonnen am 6. Okt 2006 · letzter Beitrag vom 6. Okt 2006
Antwort Antwort
Seite 2 von 2     12   
DocE

Registriert seit: 25. Mär 2004
108 Beiträge
 
#11

Re: Events in TThread

  Alt 6. Okt 2006, 13:27
So, zunächst zu :

Zitat:
Wenn hingegen die Audiokomponente asynchron läuft, dann hast Du mit einem Thread ein Problem. Aber dann benötigst Du auch keinen.
Da bin ich anderer Meinung. Also die Audiokomponente läuft asynchron (soweit ich das beurteilen kann). Sie löst immer dann, wenn der Puffer voll ist ein Event aus (reagiert intern auf MM_WIM_DATA) und dann wieder beim nächsten vollen Puffer usw. (ohne den Prozessablauf anzuhalten). Dennoch möchte ich die Verarbeitung in einem Thread durchführen, damit 1. der Hauptthread während der Verarbeitung des Puffers nicht hängt, 2. bei hängendem Hauptthread die Verarbeitung der Audiodaten nicht unterbrochen wird. Der Thread hat zusätzlich eine höhere Priorität. Ich hoffe Du stimmst / Ihr stimmt mir da zu.

Jetzt zum eigentlichen Problem:

Als ich versuchte, ein einfaches Programm nachzubauen und anstatt der Audiokomponente einen einfachen Timer einzusetzen, kam es - egal was ich machte - nicht mehr zu dem Fehler. (Sowas ärgerliches ). Also habe ich mich auf die Suche gemacht und bin (hoffentlich) fündig geworden:

Die Audiokomponente erstellt ein CallBackWin (TWinControl), welches für die Verarbeitung der MM Messages verantwortlich ist.

Delphi-Quellcode:
TCallBackWin = Class(TWinControl)
  private
    { Private declarations }
    AudioComponent : PAudioComp;

    procedure BufferFinished(var Msg: TMessage); message MM_WIM_DATA;
    procedure WaveOpenIn(var Msg: TMessage); message MM_WIM_OPEN;
    procedure WaveCloseIn(var Msg: TMessage); message MM_WIM_CLOSE;

  end;
Dieses wurde ursprünglich erzeugt mit:

Delphi-Quellcode:
  CallBackWin := TCallBackWinIn.CreateParented(TWinControl(Owner).Handle);
  CallBackWin.Visible := false;
  CallBackWin.AudioComponent := @TS;

  iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle),
                      0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Da ein Thread keinen TWinControl-Owner hat, habe ich das ganze abgewandelt in:

Delphi-Quellcode:
  
  FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu

  CallBackWin := TCallBackWinIn.CreateParented(FWindowHandle); // <- Geändert
  CallBackWin.Visible := false;
  CallBackWin.AudioComponent := @TS;

  iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle),
                      0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Die WndProc war einfach leer.

Das Problem ist (war) jetzt wohl, dass überhaupt dieses CallBackWin, also ein TWinControl im TThread erzeugt ist und Messages verarbeitet (oder sonst was Problematisches vornimmt). Mir ist wohl das Problem bekannt, dass beim Erzeugen eines Formulars in einem Thread der gleiche Fehler auftritt: "Leinwand/Bild erlaubt kein Zeichnen". Deshalb dachte ich mir: Wozu überhaupt dieses CallBackWin, wenn ich ja jetzt eine WndProc-Prozedur habe. Gesagt, getan: CallBackWin entfernt und Ereignisbehandlung in WndProc gepackt:

Delphi-Quellcode:
  FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu

  iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(FWindowHandle), // <- Geändert
                      0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );

Jetzt geht's und der Fehler tritt nicht mehr auf. Allerdings verwende ich immer noch das Application.ProcessMessages in der Schleife des Threads. Scheint also kein Problem darzustellen. Meine Tests haben bisher auch ergeben, dass das im Thread aufgerufene Application.ProcessMessages auf den Hauptthread keine Auswirkungen hat. Vielleicht bedeutet das aber auch nur, dass die Messages für den Hauptthread verloren gehen? Mir fällt jedenfalls nichts auf.


An dieser Stelle wäre nur noch das Problem zu klären:

1. Ob Application.ProcessMessages (im Thread aufgerufen) sich irgendwie schadhaft/problematisch auswirkt.
2. if 1 then Wie können wir das Problem umgehen? Sprich: "Eigene" MessageBehandlung innerhalb des Threads ermöglichen.


Das warten auf die Events in dem Thread läuft nämlich immer noch so:

Delphi-Quellcode:
while not Terminated do
  begin

    sleep(100);
    Application.ProcessMessages;

  end;

Vielleicht habt ihr ja noch eine Idee.


Grüsse
...Doc
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#12

Re: Events in TThread

  Alt 6. Okt 2006, 13:51
Hallo,
Zitat von DocE:
Ich hoffe Du stimmst / Ihr stimmt mir da zu.
Nein.
Da die Audio-Komponente asynchron arbeitet, solltest Du diese im Hauptthread erstellen und nur das Verarbeiten des Puffers in einen Thread packen. Das wäre eine saubere Lösung.
Zitat von DocE:
An dieser Stelle wäre nur noch das Problem zu klären:

1. Ob Application.ProcessMessages (im Thread aufgerufen) sich irgendwie schadhaft/problematisch auswirkt.
2. if 1 then Wie können wir das Problem umgehen? Sprich: "Eigene" MessageBehandlung innerhalb des Threads ermöglichen.
1: Application.ProcessMessages arbeitet sämtliche Nachrichten des Hauptthreads im Kontext Deines Threads ab. Ob es dann kracht oder nicht ist reines Glück.
2: Wie gesagt, ich würde nur die reine Verarbeitung in einen Thread auslagern, dann stellt sich diese Frage nicht mehr.

Gruß
xaromz
I am a leaf on the wind - watch how I soar
  Mit Zitat antworten Zitat
DocE

Registriert seit: 25. Mär 2004
108 Beiträge
 
#13

Re: Events in TThread

  Alt 6. Okt 2006, 14:02
Mahlzeit,

Zitat:
Nein.
Da die Audio-Komponente asynchron arbeitet, solltest Du diese im Hauptthread erstellen und nur das Verarbeiten des Puffers in einen Thread packen. Das wäre eine saubere Lösung.
An diese Möglichkeit hatte ich bereits gedacht, diese allerdings ausgeschlossen, weil wenn der Hauptthread hängt, gehen Puffer verloren. Das Event wird ja erst dann ausgeführt, wenn der Hauptthread wieder "idle" ist. Einfaches Beispiel:

Wird ein Sleep(10000) im Hauptthread ausgeführt (oder eine Operation, die solange dauert), wird erst im Anschluss daran, also nach 10 Sekunden das Ereignis "BufferFilled" im Hauptthread ausgeführt. Dann sind mir aber beim nächsten Event schon 9,9 Sekunden Puffer verloren gegangen.

Stellt also keine wirkliche Alternative dar.

Zitat:
Application.ProcessMessages arbeitet sämtliche Nachrichten des Hauptthreads im Kontext Deines Threads ab. Ob es dann kracht oder nicht ist reines Glück.
Kannst Du mir ein Beispiel geben, wann das z.B. problematisch werden kann. Ich konnte das bisher nämlich nicht nachvollziehen, dass es zu Problemen kommt.


Grüsse
...Doc
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#14

Re: Events in TThread

  Alt 6. Okt 2006, 14:11
Hallo,
Zitat von DocE:
Stellt also keine wirkliche Alternative dar.
Da Deine Komponente intern mit Windows-Messages arbeitet, sehe ich aber auch keine Alternative. Mit Deinem Thread hebelst Du ja nur solche Dinge wie eben ein Sleep aus. Die einzige Lösung wäre tatsächlich eine MessageProc innerhalb des Threads, die nur für diese Message da ist. Ob ein Thread eine eigene Message-Queue besitzen darf, weiß ich aber nicht.
Zitat von DocE:
Kannst Du mir ein Beispiel geben, wann das z.B. problematisch werden kann. Ich konnte das bisher nämlich nicht nachvollziehen, dass es zu Problemen kommt.
Schwierig, da solche Fehler schwer zu reproduzieren sind (Stchwort Mandelbugs). Das Problem ist aber das Selbe wie der Aufruf einer VCL-Methode aus einem Thread heraus, ohne Synchronize zu verwenden. Du weißt ja nicht, in welchem Zustand sich der Hauptthread befindent, wenn Du die Nachrichten abarbeitest.

Gruß
xaromz
I am a leaf on the wind - watch how I soar
  Mit Zitat antworten Zitat
DocE

Registriert seit: 25. Mär 2004
108 Beiträge
 
#15

Re: Events in TThread

  Alt 6. Okt 2006, 14:49
Hallo,

ich habe jetzt nochmal ein bißchen rumprobiert und vertrete weiterhin die Meinung, dass der Thread nicht die Messages des Hauptthreads "bekommt".

Dazu habe ich die MessageQueue des Hauptthreads "aufgefüllt" und mit sleep(x) die Verarbeitung angehalten, während im TThread weiter Application.ProcessMessages ausgeführt wurde. Nach dem Sleep habe ich die Messages in ein Memo schreiben lassen. Sie waren immer noch da.

Dann habe ich die Messages des TThreads und vom Hauptthread in Memos schreiben lassen (zeitgleich) und siehe da, es waren unterschiedliche. Der TThread bekam ausschließlich MM_WIM_OPEN (1x) und MM_WIM_DATA (wiederkehrend) als Message. Der Hauptthread allerdings trotz sleep, alle möglichen. Das bedeutet, dass der TThread die Messages des Hauptthreads weder empfangen, noch abgearbeitet hat.

Das Application.ProcessMessages macht im Prinzip (vereinfacht) nichts anderes als:

Delphi-Quellcode:
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;
In der Hilfe ist zu finden:

Delphi-Quellcode:
BOOL PeekMessage(

    LPMSG lpMsg,   // pointer to structure for message
    HWND hWnd,   // handle to window
    UINT wMsgFilterMin,   // first message
    UINT wMsgFilterMax,   // last message
    UINT wRemoveMsg    // removal flags
   );
Merke: Es wird also als HWND Null übergeben.

Unter Remarks:

Zitat:
PeekMessage retrieves only messages associated with the window identified by the hWnd parameter or any of its children as specified by the IsChild function, and within the range of message values given by the wMsgFilterMin and wMsgFilterMax parameters.
Wäre ja nicht so gut, aber weiter:

Zitat:
If hWnd is NULL, PeekMessage retrieves messages for any window that belongs to the current thread making the call. (PeekMessage does not retrieve messages for windows that belong to other threads.)
Nach meinem Verständnis werden bei Application.ProcessMessages ausschließlich Messages abgehandelt, die zu dem aktuellen Thread gehören, was meine Tests bestätigen würden.

Oder seht ihr einen Denkfehler?


Grüsse
...Doc
  Mit Zitat antworten Zitat
pertzschc

Registriert seit: 29. Jul 2005
Ort: Leipzig
305 Beiträge
 
Delphi 10.4 Sydney
 
#16

Re: Events in TThread

  Alt 6. Okt 2006, 15:09
Hallo DocE,

mag sein, dass ich falsch liege - ich würde Dir raten, einen Thread A zu erstellen, der intern die Audiokomponente hält (Create & Free zu den entsprechenden Zeitpunkten).

Parallel dazu einen Thread B erstellen, der eine Behandlungsmethode für das Event der Audiokomponente enthält.
Diese Behandlungsmethode würde ich auf das Event der Komponente in Thread A zuweisen.

Thread A kann dann Warterunden in seiner Execute-Methode drehen und Thread B ebenso.
Allerdings wird in Thread B dann die Execute-Methode von der Ausführung der Behandlungsmethode unterbrochen.

Um in Deiner HauptVCL-Anwendung Dinge zu verändern, benötigst einen Synchronized-Aufruf in Dein Form.
Diesen würde ich in Thread A in einer Methode implementieren und diese Methode durch einen Event von
Thread B aus auslösen.

Gruß,
Christoph
  Mit Zitat antworten Zitat
DocE

Registriert seit: 25. Mär 2004
108 Beiträge
 
#17

Re: Events in TThread

  Alt 6. Okt 2006, 15:26
Hallo Christoph,

die Audioauswertung in zwei Threads auszulagern sehe ich nicht als besonder sinnvoll an. Es funktioniert ja in einem Thread bereits wunderbar. Das Problem war ja nur, dass der Thread in einer Schleife Application.ProcessMessages ausführen muss, damit das Ereignis überhaupt ausgelöst wird. Das müßte einer Deiner beiden Threads ja trotzdem machen, sonst würde er das Ereignis nicht bekommen oder "auslaufen" (-> Terminated).

Momentan stellt sich eigentlich nur noch folgende Frage:

In meinem letzten Post habe ich folgende These aufgestellt und begründet:

Zitat:
Application.ProcessMessages verarbeitet ausschließlich Messages aus dem aufrufenden Thread und ist somit Thread-sicher.
Wenn dies so wirklich der Fall ist und das ist es meiner Meinung nach (Begründung siehe HIER), dann kann dieses problemlos in einer Schleife eines TThreads ausgeführt werden und alles ist gut.

Die Frage ist jetzt nur, ob jemand meine begründete These widerlegen kann.


Grüsse
...Doc
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#18

Re: Events in TThread

  Alt 6. Okt 2006, 17:09
Application.Processmessages ruft Prozessmessages in einer Schleife auf. ProcessMessages setzt wiederum eine private-Variable von TApplication. Das setzen dieser Variablen ist nicht mit CriticalSections abgesichert so das es dort krachen könnte. Allerdings hat dies nichts mit dem berichteten Fehler zu tun (Leinwandbild erlaubt....). Du greifst definitiv irgendwo auf grafische Elemente des Hautpthreads zu.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
DocE

Registriert seit: 25. Mär 2004
108 Beiträge
 
#19

Re: Events in TThread

  Alt 6. Okt 2006, 17:31
N'Abend SirThornberry,

das stimmt. Es gibt evtl. sogar noch ein paar weitere Punkte an denen es zu Komplikationen kommen könnte z.B. hier, falls irgendein Ereignis zugewiesen ist.

if Assigned(FOnMessage) then FOnMessage(Msg, Handled); Aber man könnte sich ja das ProcessMessages für den TThread einfach selbst schreiben durch:

(verkürzt)

Delphi-Quellcode:
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin

  TranslateMessage(Msg);
  DispatchMessage(Msg);

end;
oder

(fast 1:1)

Delphi-Quellcode:
procedure MyProcessMessageImThread(var Msg: TMsg);
begin

  while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
  begin
    
    if Msg.Message <> WM_QUIT then // bekommt ein Thread vermutlich nie, aber gut
    begin

      // trifft alles für Threads normalerweise nicht zu
      {if not IsHintMsg(Msg) and not IsMDIMsg(Msg) and
       not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then}
 

        TranslateMessage(Msg);
        DispatchMessage(Msg);

    end;
  
  end;

end;
Ich denke, dann sollte es zu keinem Problem kommen.

Irgendwie kursiert wohl allgemein das Gerücht, dass ein TThread mit Application.ProcessMessages auch auf die Messages des Hauptthreads zugreift. Dies ist jedoch nicht richtig! Jeder Thread hat praktisch seine eigene MessageQueue. Mit diesem Vorurteil sollte einmal aufgeräumt werden.


Grüsse
...Doc
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:44 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz