Entwickler-Ecke
Dateizugriff - Synchronisation von Threads
Pellaeon - Mo 05.03.12 22:02
Titel: Synchronisation von Threads
Hiho,
folgende Ausgangslage: ich habe eine DLL. Diese startet einen Thread, welcher erst einmal nichts macht. Wenn eine entsprechende Anforderung eingeht, öffnet der Thread eine VCL-Form modal. Damit das ganze klappt, müssen die Threads synchronisiert werden. Dazu wollte ich TMutex und TConditionVariableMutex benutzen.
Der Hauptthread nimmt den Mutex im Konstruktor in Beschlag. Der Thread wartet per Condition Variable darauf.
Hauptprozess:
-------------
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TFormThread.showForm(); begin m_show:= true; m_wakeUp:= true; m_condVar.Release();
while m_wakeUp do m_condVar.WaitFor(m_mutex); m_mutex.Acquire(); m_wakeUp:= false; end; |
Thread:
------
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: 27: 28: 29: 30:
| procedure TFormThread.Execute(); begin Application.Initialize(); Application.Title:='Delphi Window'; m_form:= TViewer.Create(Application);
while True do begin while not m_wakeUp do m_condVar.WaitFor(m_mutex);
m_mutex.Acquire();
m_wakeUp:= false;
if (not m_show) then break;
m_mutex.Release(); m_condVar.Release(); m_form.ShowModal(); end;
Application.Terminate(); m_form.Destroy(); m_form:= nil; m_mutex.Release(); m_condVar.Release(); end; |
Nun soll der Thread erst etwas machen. Darauf soll der Hauptprozess warten und erst wenn der Thread das Signal gibt, weitermachen.. Beim ersten mal klappt das auch immer, aber sobald ich die Condition-Variable mehrmals in beide Richtungen will, klappt das nicht. Irgendwann hängt eine Seite immer. Das starten klappt, aber wenn der Dialog dann beendet wird, sendet der Hauptthread irgendwann ein condVar.release, aber das kommt beim WaitFor nicht an. Die Doku von embarcadero.com ist leider dazu grottig. Der kann man zu diesem Thema nichts weiter entnehmen.
Evtl. hat ja hier noch jemand eine Idee?
VG
Pellaeon
Moderiert von
Gausi: B- durch Delphi-Tags ersetztModeriert von
Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 05.03.2012 um 22:24
Delphi-Laie - Mo 05.03.12 23:17
Helfen die Stichwörter "criticalsection" und "Synchronisation" weiter?
delfiphan - Mo 05.03.12 23:21
Du hast eine Race-condition und wohl auch ein paar Blöcke, die als atomare Operation ausgeführt werden müssten, aber nicht werden. Überleg dir die Szenarien nochmals genau. Du kannst dir das mit Context-switches vorstellen (sprich 1 Prozessor) - der Switch kann praktisch an jeder Stelle auftauchen.
Ich hätte das jetzt mit zwei autoresetevents gemacht.
Aber Application in einem Thread sieht ohnehin sehr suspekt aus. Mit viel Glück mag das halbwegs funktionieren, aber es ist sicher nicht darauf ausgelegt...
jaenicke - Mo 05.03.12 23:51
Jegliche Zugriffe auf VCL-Objekte, insbesondere das Erstellen von Formularen usw., muss im Hauptthread passieren. Alles andere geht nur zufällig gut, wenn überhaupt...
Dass du also so Probleme bekommst, ist klar und das lässt sich auch nicht anders lösen als sämtliche VCL-/Fenster-/Application-Zugriffe im Kontext des Hauptthreads zu machen.
Delphi-Laie - Di 06.03.12 00:05
Moderiert von
Narses: Komplett-Zitat des letzten Beitrags entfernt.
Aber dafür gibt es doch das synchronize?! Damit machte ich bisher gute Erfahrungen, ohne wird es allerdings natürlich ein Hornberger Schießen.
Die Bezeichnung "Hauptthread" (o.ä.) gefiel übrigens Luckie überhaupt nicht. Prinzipiell sind die Threads n.m.W. auch gleichberechtigt, es gibt lediglich einen Startthread, der sich aber keinesfalls wie ein roter Faden durch den Programmablauf ziehen muß (oder doch, für die Nachrichtenschleife, damit die visuellen Komponenten bedienbar bleiben?).
jaenicke - Di 06.03.12 00:26
Delphi-Laie hat folgendes geschrieben : |
Aber dafür gibt es doch das synchronize?! |
Damit wird der Code ja im Kontext des Hauptthreads ausgeführt.
Delphi-Laie hat folgendes geschrieben : |
Die Bezeichnung "Hauptthread" (o.ä.) gefiel übrigens Luckie überhaupt nicht. |
Ich halte es für genau die passende Bezeichnung.
Und ich frage mich wie er main thread (wie er im Quelltext der RTL von Delphi heißt) besser übersetzen würde. :nixweiss:
Zitat: |
System.pas 1:
| MainThreadID: TThreadID; | |
delfiphan - Di 06.03.12 00:27
In der Delphiwelt gibt es eine globale Variable MainThreadID, welches dem GetCurrentThreadId bei der Initialisierung entspricht. Daher ist der Ausdruck klar definiert und in der System.pas verankert, daher ist der Begriff sicherlich nicht daneben.
//Edit: @jaenicke: Da warst du wohl ein bisschen schneller als ich ;)
Wie auch immer: Wenn du Fensterchen in einem Thread darstellen musst, dann solltest du nicht auf VCL aufbauen. Musst du das denn überhaupt wirklich können? Was ist denn die genaue nicht technische Anforderung, und wieso hast du diese Lösung gewählt?
Pellaeon - Mi 07.03.12 14:20
Ich habe eine Delphi-GUI. Diese soll von einem C#-Programm aus gestartet werden. Dazu habe ich die Delphi-GUI in eine DLL ausgelagert. Da die GUI eine separate Nachrichtenschleife benötigt, muss ich sie in der DLL in einen Thread auslagern, ansonsten würde die DLL das C#-Programm blockieren.
CriticalSection bringt mir nix, da ich keinen Codeabschnitt habe, der von mehreren Threads durchlaufen wird. Es geht eher um ein sinnvolles abstimmen der Threads beim Starten und Beenden der GUI und um Synchronisieren von Datenzugriffen.
Das Auslagern der GUI in einen extra Thread macht nicht unbedingt immer Probleme. In meinem Fall ist die Delphi-GUI ja komplett in dem neuen Thread, ich habe keine Aufteilung das ein Teil in Thread 1 und ein andere in Thread 2 läuft.
VG
Pellaeon
Moderiert von
Narses: Beiträge zusammengefasst Delphi-Laie hat folgendes geschrieben: |
Die Bezeichnung "Hauptthread" (o.ä.) gefiel übrigens Luckie überhaupt nicht. Prinzipiell sind die Threads n.m.W. auch gleichberechtigt, es gibt lediglich einen Startthread, der sich aber keinesfalls wie ein roter Faden durch den Programmablauf ziehen muß (oder doch, für die Nachrichtenschleife, damit die visuellen Komponenten bedienbar bleiben?). |
Soweit ich mich erinner, unterscheidet ein Win32-Programm schon in den Hauptprozess und Threads. Kille ich den Hauptprozess, werden auch die Threads hart beendet.
Dein Gleichheitsprinzip für Threads hat bspw. in Java gültigkeit, soweit ich mich erinner. Da haut das hin, solange ien Thead etwas macht, ist auch die Anwendung noch aktiv.
jaenicke - Mi 07.03.12 14:51
Pellaeon hat folgendes geschrieben : |
Ich habe eine Delphi-GUI. Diese soll von einem C#-Programm aus gestartet werden. Dazu habe ich die Delphi-GUI in eine DLL ausgelagert. Da die GUI eine separate Nachrichtenschleife benötigt, muss ich sie in der DLL in einen Thread auslagern, ansonsten würde die DLL das C#-Programm blockieren. |
Also ich brauchte da keinen expliziten neuen Thread. :gruebel:
jaenicke - Mi 07.03.12 15:07
ShowModal blockiert, ja. Aber ein normales Show nicht.
Pellaeon - Mi 07.03.12 16:02
Ein normales show hat aber keine Nachrichten-Schleife!!! Dafür ist ja das Application.run da.
Wenn ich in der DLL nur show aufrufe, startet die GUI, die DLL-Funktion kehrt zurück und die GUI macht nichts, weil die eingehenden Nachrichten nicht verarbeitet werden.
Oliver Maas - Mi 07.03.12 18:20
Nabend. Kann sein, dass ich hier auf dem Schlauch stehe, aber ich kapier das mit der DLL schon nicht so recht.
Die Kombination VCL GUI + dll kommt mir etwas seltsam vor (kann aber natürlich trotzdem sein, dass das irgendwie geht).
Kann man von C# nicht sowas wie CreateProcess aufrufen?
Dann hat man eh 2 getrennte Prozesse.
Notfalls kann man sogar was mit Sockets machen (dann schickt man von C# was, und die andere App setzt .Visible := true oder so irgendwie).
Olli
Pellaeon - Mi 07.03.12 19:10
Oliver Maas hat folgendes geschrieben : |
Nabend. Kann sein, dass ich hier auf dem Schlauch stehe, aber ich kapier das mit der DLL schon nicht so recht.
Die Kombination VCL GUI + dll kommt mir etwas seltsam vor (kann aber natürlich trotzdem sein, dass das irgendwie geht). |
Warum sollte das nicht gehen und warum sollte das seltsam sein? Man pappt doch auch GUIs in ActiveX-Controls usw. Ist ja dasselbe Prinzip.
Oliver Maas hat folgendes geschrieben : |
"Kann man von C# nicht sowas wie CreateProcess aufrufen?
Dann hat man eh 2 getrennte Prozesse. |
Das geht, aber dann habe ich zwei unabhängige Programme, das will ich nicht. Ist ungünstig für die Anwendung, ohne das jetzt ins Detail zu erläutern.
Oliver Maas hat folgendes geschrieben : |
Notfalls kann man sogar was mit Sockets machen (dann schickt man von C# was, und die andere App setzt .Visible := true oder so irgendwie). |
Das geht, macht aber gehörig Mehraufwand, wenn ich jetzt erst noch anfangen müsste, mit Sockets zu arbeiten.
delfiphan - Mi 07.03.12 19:41
Dein .NET Programm hat eine Nachrichtenschleife (angenommen die hat eine GUI). Die reicht doch.
Wenn du keine Nachrichtenschleife hast, dann könntest du noch folgendes probieren: Erzeuge in .NET einen Thread und lade von dort aus deine Delphi-DLL. Im Prinzip sollte dann die ganze Delphi-Initialisierung auf dem Thread stattfinden; für deine Delphi-DLL sieht es aus, als wäre der .NET Thread der MainThread und er sollte der MainThreadID auch die ID des .NET-Threads geben. Ein gutes Gefühl habe ich dabei nicht, aber du könntest es ja mal probieren...
Pellaeon - Mi 07.03.12 21:04
Hiho delphifan,
die reicht nicht. .NET ist doch eine ganz andere GUI-Lib als die VCL. Klar, irgendwo geht es irgendwann zur WinAPI und PeekMessage, TranslateMessage usw., aber das Handling der Fenster, die Zuordnung von Nachrichten zu Funktionen und Methoden, usw. das macht ja alles die entsprechende GUI-Bibliothek. Und die VCL wird das anders machen als das .NET-Framework. Wie soll z.B. .NET eine einkommende Nachricht auf Delphi-Form-Methoden zuordnen.
Mein Problem ist ja nicht die GUI im Thread. Das funktioniert wurnderbar. Mein Problem sind die (rotz) DelpiXE-Synchklassen. Die machen nicht, was ich will. Ich habe ein ähnliches Prinzip mit Mutex und Condition-Variable für einen anderen Sachverhalt in C++ mit Boost programmiert. Das funzt 1A. Die Delphi-Klassen verhalten sich aber anders, die Doku gibt dazu nicht viel Auskunft. Da liegt mein Problem. Ich hab jetzt aber erstmal einen Workaround ohne die Synch-Klassen und nur mit einer einer boolean sowie Sleep() gebaut. Das funktioniert zumindest, auch wenn es mit den Synch-Klassen schöner gewesen wäre.
jaenicke - Mi 07.03.12 21:18
Pellaeon hat folgendes geschrieben : |
Ein normales show hat aber keine Nachrichten-Schleife!!! Dafür ist ja das Application.run da.
Wenn ich in der DLL nur show aufrufe, startet die GUI, die DLL-Funktion kehrt zurück und die GUI macht nichts, weil die eingehenden Nachrichten nicht verarbeitet werden. |
Das kam mir vorhin schon komisch vor, dass das nicht gehen soll, aber ich war im Büro und konnte mich nicht weiter drum kümmern. Ich habe es jetzt zu Hause ausprobiert. Das kann ich in keiner Weise reproduzieren. Test-DLL in Delphi + C#-Anwendung inkl. Quelltext liegen im Anhang. Ich zeige einfach ein Formular an und alles funktioniert. :nixweiss:
Pellaeon - Mi 07.03.12 23:09
Erstmal danke für deine Mühe!
Ich werde das morgen mal mit meiner Form so testen und dann wieder posten, ob es ging oder nicht. Soweit ich mich erinner, dachte ich aber ich hätte das mit nur "show" schon getestet. Naja wie gesagt, ich probier das nochmal und berichte dann :)
delfiphan - Do 08.03.12 09:07
Pellaeon hat folgendes geschrieben : |
Klar, irgendwo geht es irgendwann zur WinAPI und PeekMessage, TranslateMessage usw., (...) Wie soll z.B. .NET eine einkommende Nachricht auf Delphi-Form-Methoden zuordnen. |
Du gibst dir die Antwort ja gleich selbst. Über DispatchMessage werden die richtigen Callbacks aufgerufen -- wenn das eine Message für einen Delphi-Knopf ist, dann geht es ins Delphi rein.
Du kannst von Delphi aus ja auch eine Win32 MessageBox anzeigen und deine Delphi-UI im Hintergrund friert auch nicht ein. Das liegt daran, dass auch die Win32 MessageBox eine Nachrichtenschleife hat.
Delphi-Laie - Do 08.03.12 11:07
Pellaeon hat folgendes geschrieben : |
Soweit ich mich erinner, unterscheidet ein Win32-Programm schon in den Hauptprozess und Threads. Kille ich den Hauptprozess, werden auch die Threads hart beendet. |
Meinst Du tatsächlich "Hauptprozess" oder doch eher "Hauptthread"? Es gibt nämlich keinen Hauptprozeß, und wenn der Prozeß beendet wird, dann logischerweise auch alle seine Threads (für die er ja eine Art Container darstellt).
Pellaeon hat folgendes geschrieben : |
Dein Gleichheitsprinzip für Threads hat bspw. in Java gültigkeit, soweit ich mich erinner. Da haut das hin, solange ien Thead etwas macht, ist auch die Anwendung noch aktiv. |
Das ist doch dann aber ein Konstruktionsmerkmal von Windows, das Java ausnutzt, denn Threads gibt es auch außerhalb Javas und seiner Compilate. Ob Delphi das für seine Compilate auch umzusetzen imstande ist, weiß ich nicht.
Pellaeon - Do 08.03.12 21:34
@jaenicke: klappt bei meiner Form nicht. Die bleibt weiß und tut nichst mehr. Ich habe testweise einfach in einer DLL-Funktion alles auskommentiert, meine Form erstellt und show aufgerufen mit dem eben beschriebenem Ergebnis.Gehe ich den Weg mit dem Thread und einem Application-Objekt, dann funktioniert es.
delfiphan - Do 08.03.12 22:18
Falls du in der Delphiapplikation Threads brauchst, sollte die .NET Applikation ab und zu mal CheckSynchronize von Delphi ausführen.
Ansonsten können wir ohne Code wohl nicht weiterhelfen.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!