![]() |
Delphi-Version: 12 Athens
Variable eines Record geht beim Multithreading verloren
Hallo,
ich versuche mich gerade am Multithreading mit delphi und bin gerade auf ein seltsames Verhalten gestoßen, dass ich nicht verstehe. Ich habe derzeit folgenden Code:
Delphi-Quellcode:
procedure TForm1.btnEventClick(Sender: TObject);
begin TMessageManagerClass.DefaultManager.SubscribeToMessage(MyMessage, procedure(const Sender: TObject; const M: System.Messaging.TMessage) var bstr : string; p : string; i : string; begin bstr := (TThread.CurrentThread.ThreadID = MainThreadID).ToString(); p := MyMessage(M).Value.Str; i := MyMessage(M).Value.I.ToString(); // TThread.Queue(nil, procedure // begin // edtEvent.Text := p + i + '/' + bstr + '/' + (TThread.CurrentThread.ThreadID = MainThreadID).ToString(); // end); TThread.Queue(nil, procedure begin edtEvent.Text := MyMessage(M).Value.Str + MyMessage(M).Value.I.ToString() + '/' + bstr + '/' + (TThread.CurrentThread.ThreadID = MainThreadID).ToString(); end); end); EventThread.Create(); end;
Delphi-Quellcode:
MyRecord = record
Str: string; I: Integer; end; MyMessage = class(TMessage<MyRecord>) end; EventThread = class(TThread) protected procedure Execute; override; end;
Delphi-Quellcode:
Ich sehe in der Variable p, dass ein zufälliger Buchstabe in dem string steht. Wenn der Code dann aber im TThread.Queue ankommt, dann ist MyMessage(M).Value.Str = ''.
procedure EventThread.Execute;
var rec : MyRecord; begin while True do begin rec.str := Chr(ord('a') + Random(26)); rec.I := Random(9); TMessageManager.DefaultManager.SendMessage(self, MyMessage.Create(rec)); Sleep(1000); end; end; Wenn ich in TThread.Queue aber auf die Variable p direkt zugreife (siehe auskommentierter Code), dann ist der Char wie erwartet im edit zu sehen. Kann mir jemand erklären, warum das so passiert? Edit 1: Ich habe das ganze mit Synchronize getestet. Hier ist es egal, ob ich den String in einer Variablen zwischenspeichere, oder direkt aus dem Record auslese. In beiden Fällen wird der Char in den Edit geschrieben. |
AW: Variable eines Record geht beim Multithreading verloren
Der Parameter M ist innerhalb der anonymen Methode nur bis zu ihrem Ende gültig. Der Code im Queue-Aufruf findet aber später statt (bei Synchronize ist das nicht der Fall).
Wie du schon bemerkt hast, kannst du das über eine lokale Variable lösen, die dann auch innerhalb der anonymen Queue-Methode gültig ist. Das kann anstatt der einzelnen Variablen p und i auch eine Variable vom Typ MyRecord sein.
Delphi-Quellcode:
procedure TForm1.btnEventClick(Sender: TObject);
begin TMessageManagerClass.DefaultManager.SubscribeToMessage(MyMessage, procedure(const Sender: TObject; const M: System.Messaging.TMessage) var bstr : string; my: MyRecord; begin bstr := (TThread.CurrentThread.ThreadID = MainThreadID).ToString(); my := MyMessage(M).Value; TThread.Queue(nil, procedure begin edtEvent.Text := my.Str + my.I.ToString + '/' + bstr + '/' + (TThread.CurrentThread.ThreadID = MainThreadID).ToString(); end); end); EventThread.Create(); end; |
AW: Variable eines Record geht beim Multithreading verloren
Erstmal, TMessageManager ist nicht threadsafe.
Wenn man was drumrum synchronisiert, zum Beispiel um das SendMessage, dann sollte es aber funktionieren. |
AW: Variable eines Record geht beim Multithreading verloren
Zitat:
Delphi-Quellcode:
Kannst du konkrete Fälle aufzeigen bei denen TMessageManager nicht thread-safe ist? (Ich will ja gar nicht ausschließen dass es die gibt.)
procedure TMessageManager.SendMessage(const Sender: TObject; AMessage: TMessage; ADispose: Boolean);
var Subscribers: TListenerList; begin if AMessage = nil then raise Exception.CreateRes(@SArgumentInvalid); TMonitor.Enter(FListeners); try try if FListeners.TryGetValue(AMessage.ClassType, Subscribers) then Subscribers.SendMessage(Sender, AMessage); finally if ADispose then AMessage.Free; end; finally TMonitor.Exit(FListeners); end; end; |
AW: Variable eines Record geht beim Multithreading verloren
Ja, ich benutze das auch mit Queue und Synchronize, das macht es ja threadsafe.
Warum es nicht threadsafe ist, da ist Dalija sehr stark der Meinung, dass es das auf gar keinen Fall ist, und hat deshalb NxHorizon rausgebracht ![]() ![]() Das kommt so an mehreren Stellen und verschiedenen Threads, wird wohl was dran sein. Ich habe jetzt explizit auch keine Probleme damit, aber synchronisiere auch auch alles, da wo es nötig ist. Vermutlich ist gemeint, dass man sich von außen um die Synchronisation kümmern muss, und das nicht implizit in TMessage eigebaut ist. |
AW: Variable eines Record geht beim Multithreading verloren
Zitat:
Setzt Delphi strings auf '' wenn sie out of scope gehen und bei Integern wird nur der Speicher nur für Wiederverwendung freigegeben, aber nicht auf 0 gesetzt? Wenn das Zutrifft, ist es dann einfach nur Zufall, dass der Speicher des Integer einfach nicht wiederverwendet wurde und deshalb die Zahl die gleiche ist? |
AW: Variable eines Record geht beim Multithreading verloren
Genau. Gerade auch im Debugger 'halten' Variablen(-inhalte) manchmal länger als sie müssten.
Später dann auf einem Kundenrechner tun sie das dann plötzlich nicht mehr. |
AW: Variable eines Record geht beim Multithreading verloren
Bzw. jetzt hat Emba gesagt, dass Variblen garnicht mehr so lange halten müssen, wie man denkt und wie bisher es "immer" war.
Ich würde ja den Thread zu suchen, aber .... |
AW: Variable eines Record geht beim Multithreading verloren
Zitat:
![]() In deinem Beispiel werden p und i im Closure erfasst weil sie als Variablen deklariert sind. Der Parameter wird zwar auch erfasst, ist aber eine Instanz einer TMessage-Klasse, die nach dem SendMessage wieder freigegeben wird. Daher enthält sie beim späteren Ausführen der Queue-Methode einen undefinierten Inhalt. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:45 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