![]() |
TInterlocked außerhalb eines TThreads?
Ich säubere gerade meinen Code und ersetze an ein paar Stellen Inc() durch TInterlocked.Add().
Hat TInterlocked überall den Vorteil den der Befehl bringen sollte (Sicherheit beim Schreiben der Variable) oder muss das zwingend aus einem TThread aufgerufen werden? |
AW: TInterlocked außerhalb eines TThreads?
Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Zitat:
Delphi-Quellcode:
Unit191.pas.39: TInterlocked.Increment(I);
005FA2E4 8D45F8 lea eax,[ebp-$08] 005FA2E7 BA01000000 mov edx,$00000001 005FA2EC E8B725F1FF call TInterlocked.Add 005FA2F1 8945F4 mov [ebp-$0c],eax Unit191.pas.40: Inc(I); 005FA2F4 FF45F8 inc dword ptr [ebp-$08] |
AW: TInterlocked außerhalb eines TThreads?
Ich habe das gerade mal geprüft und eine etwas längere Prozedur gestartet die gewisse Dinge tut - und das 3550 Mal.
Wenn es einen Unterschied gibt, dann liegt der im Millisekundenbereich. Mit Inc() war der Prozess in 45 Sekunden erledigt, mit TInterLocked.Add in 44. Und selbst wenn es umgekehrt wäre, würde ich trotzdem TInterLocked aktuell bevorzugen. Ich hatte noch sehr viele Stellen mit Inc und das in einer multithreaded Anwendung. |
AW: TInterlocked außerhalb eines TThreads?
Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Zitat:
Das würde ja das hier revidieren: Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Zitat:
Und umgekehrt denkt jemand vielleicht, dass der Code schon threadsicher ist, obwohl nur die eine Variable threadsicher verändert wird und der Rest nicht geschützt ist. Wenn ich so unseren Code durchschaue, sehe ich nicht viele Stellen, an denen solch ein Interlocked-Befehl ausreicht. Den verwenden wir zur Synchronisation mit anderen Threads, aber für sich genommen würde der nur an ein oder zwei Stellen etwas bringen. Um eine saubere Trennung der Threads usw. kommst du mit so etwas nicht herum... |
AW: TInterlocked außerhalb eines TThreads?
Zitat:
|
AW: TInterlocked außerhalb eines TThreads?
Wenn die Threads sauber implementiert sind, macht es keinen Sinn ohne konkreten Anlass einfach irgendetwas zu locken.
|
AW: TInterlocked außerhalb eines TThreads?
Meine Threads haben alle schreibenden Zugriff auf ein paar in einem Record befindlichen Variablen.
Ich könnte das auch ohne locken machen nur das wäre echt eine super dämliche Lösung: jeder Thread schreibt in seine eigenen, lokalen Variablen und erst bein Thread-Destroy schreibe ich den lokalen Wert in ein globale Variable. Somit hätte ich statt einem ständigem Locken nur noch einmal Locken. Das ist aber böse, hässlich und einfach nicht gut also mache ich den Text mal grau :P |
AW: TInterlocked außerhalb eines TThreads?
Das heißt jeder Thread aktualisiert gegebenenfalls mehrere Variablen hintereinander?
Dann würde ich eine Klasse daraus machen und schlicht mit TMonitor.Enter...TMonitor.Exit arbeiten. Oder geht es immer nur um einzelne Werte? Dann würde ich ebenfalls eine Klasse daraus machen (geht aber auch als Record) und Setter benutzen (inline deklarieren wegen der Performance), die sich dann um die Locks kümmern. Die können das dann auch mit TInterlocked machen, aber du musst das nicht überall im Quelltext machen. Beides sind saubere Lösungen. |
AW: TInterlocked außerhalb eines TThreads?
Zitat:
Aktuell ist es schlicht so: es existiert ein Record und auf die einzelnen Werte greife ich zu une modifiziere sie mit TInterLocked. Und damit ich nur an einer Stelle im Code die Variable selber habe ist es so gelößt
Delphi-Quellcode:
Wenn es auf einfache Art und Weise besser geht (wovon ich ausgehe) dann nehme ich diese Kritik gerne an!// unit _globals.pas type TTestRecord = packed record test, hallo, huhu: Int64; end; var aTestRecord: TTestRecord; // Unit _enums.pas type TTestEnum = (test, hallo, huhu); // unit log_utils.pas procedure test(const aTestEnum: TTestEnum; iIncrement: Integer = 1); begin case aTestEnum do TTestEnum.test: TInterLocked.Add(aTestRecord.test, iIncrement); TTestEnum.hallo: TInterLocked.Add(aTestRecord.hallo, iIncrement); TTestEnum.huhu: TInterLocked.Add(aTestRecord.huhu, iIncrement); end; end; // unit... überall da wo ich es brauche test(TTestEnum.hallo, 5); test(TTestEnum.huhu); |
AW: TInterlocked außerhalb eines TThreads?
Sehr einfach:
Delphi-Quellcode:
Deine Lösung empfinde ich als mehr Aufwand als diese (zusätzliche Variable, zusätzliches Set, zusätzliches case, ...). Und langsamer ist deine durch die zusätzliche Fallunterscheidung und das fehlende inline auch. Dazu kommt noch, dass die Funktionalität so auf drei Units verteilt ist.
// unit _globals.pas
type TTest = class private class var FTest, FHallo, FHuhu: Int64; public class procedure IncTest(const AValue: Int64 = 1); inline; class procedure IncHallo(const AValue: Int64 = 1); inline; class procedure IncHuhu(const AValue: Int64 = 1); inline; class property Test: Int64 read FTest; class property Hallo: Int64 read FHallo; class property Huhu: Int64 read FHuhu; end; { TTest } class procedure TTest.IncHallo(const AValue: Int64 = 1); begin TInterLocked.Add(FHallo, AValue); end; class procedure TTest.IncHuhu(const AValue: Int64 = 1); begin TInterLocked.Add(FHuhu, AValue); end; class procedure TTest.IncTest(const AValue: Int64 = 1); begin TInterLocked.Add(FTest, AValue); end; // unit... überall da wo ich es brauche procedure Test1; begin TTest.IncHallo(5); TTest.IncHuhu; // lesen: ShowMessage(IntToStr(TTest.Hallo)); end; Zusätzlicher Vorteil: Von außen gibt es durch die Properties keinen schreibenden Zugriff mehr auf die Felder, trotzdem kann lesend direkt auf die Felder zugegriffen werden (da es keinen Getter gibt, werden direkt die Werte verwendet). // EDIT: Nebenbei, falls die Unitnamen echt sind: Unterstriche in Unitnamen und Bezeichnern sind in Delphi unüblich. In Delphi ist CamelCase üblich und für Unitnamen auch Punkte. (Empfinde ich auch als deutlich besser lesbar.) Bei uns würden die Units z.B. heißen (wenn es allgemeine Units für mehrere Projekte sind, daher Common): Common.Utils.Logging.pas, Common.Types.Logging.pas, ... (Im Unterverzeichnis common\utils bzw. common\types) Und so allgemeine Namen wie _globals oder so würde es bei uns nicht geben, da das nichts über die Funktion aussagt. Die Klasse hieße bei uns dann auch z.B. TLogging und wäre die einzige in der Unit. Auf die Weise werden auch die Units nicht so groß und übersichtlich und man findet auch direkt die Units zu den Klassen. |
AW: TInterlocked außerhalb eines TThreads?
Ich gucke mir das mal ganz genau an um es auch zu verstehen.
Aktuell verwende ich solche Klassen-Konstrukte häufig. Record statt Class und statt inline verwende ich aber static (weil ich es damals so gefunden habe). Aber ich gucke mal wie ich das bei mir umgesetzt bekomme. Ich habe schon gelesen, dass inline wohl für kleine Prozeduren/Funktionen von Vorteil sein soll. Wie sieht das denn mit Prozeduren/Funktionen aus, die 200+ Zeilen lang sind? |
AW: TInterlocked außerhalb eines TThreads?
Zitat:
Mit static gibt es kleinste Optimierungen durch die Tatsache, dass EAX nicht als Self-Pointer benötigt wird. Das sind aber nur einzelne Assemblerbefehle, die wegfallen. Bei größeren Funktionen bringt inline nicht mehr so viel, denn wenn eine Funktion z.B. nur 20 Assemblerbefehle enthält wie in diesem Fall bei IncTest usw. macht es schon einen Unterschied, ob noch 10 für den Methodenaufruf hinzukommen. Hat die Funktion 200 Zeilen Pascalcode (schon sehr viel übrigens, da schlägt jede Metrikanalyse für Quelltext Alarm), relativiert sich das, weil der Code für den Aufruf im Verhältnis zum Methodeninhalt sehr klein ist. |
AW: TInterlocked außerhalb eines TThreads?
Wieso gibt es hier eigentlich noch keinen "Danke"-Button?
Zu deinem Code oben. Ich habe es jetzt erst einmal vorläufig so gemacht:
Delphi-Quellcode:
Ist das trotzdem schon besser als vorher?
// unit _globals.pas (vorläufig bleibt es bis Zeit habe und richtig umbaue)
type TTestRecord = packed record test, hallo, huhu: Int64; end; var aTestRecord: TTestRecord; // Unit _enums.pas (das ist weg) // type // TTestEnum = (test, hallo, huhu); // unit log_utils.pas procedure test(iTarget: Int64; iIncrement: Integer = 1); begin TInterLocked.Add(iTarget, iIncrement); end; // unit... überall da wo ich es brauche test(aTestRecord.hallo, 5); test(aTestRecord.huhu); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:08 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