Autor Beitrag
ChZiegelt
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 06.07.09 12:34 
Hallo zusammen,

ich habe ein kleines Problem mit Strings in D7.
Ich möchte gerne zwischen Threads Strings per Windows Message versenden.

Dazu habe ich bisher Objekte erzeugt, die den String speichern und dann die Adresse des Objektes versendet.
Dieser Weg klappt generell auch gut, prinzipiell möchte ich diesen Weg nicht ändern.

Nun gibt es aber Probleme mit dem String datentyp - ich möchte eine Datenstruktur verwenden, die ohne die optimierungen von Delphi funktioniert.
Also z.B. eine CharArray oder ein ByteArray.
Ich gebe einen String hinein, und er wird als Array gespeichert, ohne dass der original String dabei verändert wird, oder verloren geht.

Vor Allem bleibt mir das Problem, aus dem Array hinterher wieder EFFIZIENT einen String zu erstellen - als Kopie. Das Objekt soll weiter unangetatstet bleiben.
Ich möchte hierbei nicht mit einer Schleife mystring := myString + Arr[i]; nutzen.

Hat hier jemand Erfahrungen, ob ein ByteArray oder ein CharArray besser wäre, und wie erstelle ich einen normalen String aus dem Array.

Vielen Dank im Voraus.
Christian

Der gewünschte Weg
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
  TCharObj = class(TMsgDataType)
  private
    FData: TCharArr;

    function getData: TCharArr;
    procedure setData(const Value: TCharArr);
    function getStringData: String;

  public
    constructor Create(const Data: TCharArr);
    constructor Create(const Data: String);

    property Data: TCharArr read getData write setData;
    property StringData: String read getStringData;

    function getSize: Integer; override;
    function Clone: TCharObj;
  end;



Der bisherige Weg:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
  TStrObj = class(TMsgDataType)
  private
    FData: PChar;

    function getData: String;
    procedure setData(const Value: String);
  public
    { Es soll IMMER ein eigener String, nicht eine neue Referenz sein }
    property Data: String read getData write setData;

    function getSize: Integer; override;

    constructor Create(const Data: String);
    destructor Destroy; override;

    function Clone: TMsgDataType; override;

  end;


Moderiert von user profile iconChristian S.: Beiträge zusammengefasst

Eine kleine Erweiterung,

vielleicht habe ich das Problem aber auch schon quasi gelöst und sehe es nur nicht, hier einmal der komplette alte weg:

ausblenden volle Höhe 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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
type
{*------------------------------------------------------------------------------
  Grundlage jeden Objektes das per Message verschickt wird

  Type        : TMsgDataType
  Author      : Christian Ziegelt
  Date        : 03-Okt-2008
------------------------------------------------------------------------------*}

  TMsgDataType = class(TInterfacedObject, ICloneableData)
  protected
    FTimeStamp: TDateTime;
    FTickStamp: DWord;
    procedure setTickStamp(const Value: DWord);
    procedure setTimestamp(const Value: TDateTime);

  public
    constructor Create;

    property TimeStamp: TDateTime read FTimeStamp write setTimestamp;
    property TickStamp: DWord read FTickStamp write setTickStamp;

    function getSize: Integer; virtualabstract;
    function Clone: TMsgDataType; virtualabstract;
  end;



  TStrObj = class(TMsgDataType)
  private
    FData: PChar;

    function getData: String;
    procedure setData(const Value: String);
  public
    { Es soll IMMER ein eigener String, nicht eine neue Referenz sein }
    property Data: String read getData write setData;

    function getSize: Integer; override;

    constructor Create(const Data: String);
    destructor Destroy; override;

    function Clone: TMsgDataType; override;

  end;


implementation

{ TStrObj }

{*------------------------------------------------------------------------------

  Procedure   : Clone()
  Author      : Christian Ziegelt
  Date        : 28-Apr-2008

  @return ResultDescription
------------------------------------------------------------------------------*}

function TStrObj.Clone: TMsgDataType;
begin
  try
    try

      Result := TStrObj.Create(Self.Data);

    except
      On E: Exception do
      begin
        SiMain.EnterMethod(lvDebug, Self, 'Clone()', []);
          SiMain.LogException(E, E.Message);
        SiMain.LeaveMethod(lvDebug, Self, 'Clone()');
      end;
    end;
  finally
    { finally free unneeded resources  }
  end;

end;



{*------------------------------------------------------------------------------

  Procedure   : Create()
  Author      : Christian Ziegelt
  Date        : 28-Apr-2008

  @param Data   ParameterDescription
  @return ResultDescription
------------------------------------------------------------------------------*}

constructor TStrObj.Create(const Data: String);
var
  len: Integer;
begin
  inherited Create;

  try
    try
      { Bei dieser Zuweiseung wird schon eine Kopie angelegt }
      Self.Data := Data;

    except
      On E: Exception do
      begin
        SiMain.EnterMethod(lvDebug, Self, 'Create()', []);
          SiMain.LogException(E, E.Message);
        SiMain.LeaveMethod(lvDebug, Self, 'Create()');
      end;
    end;
  finally
    { finally free unneeded resources  }
  end;

end;



{*------------------------------------------------------------------------------

  Procedure   : Destroy()
  Author      : Christian Ziegelt
  Date        : 28-Apr-2008

  @return ResultDescription
------------------------------------------------------------------------------*}

destructor TStrObj.Destroy;
begin
  try
    try

      { Die CloneRessource darf nicht befreit werden, sie wird woanders benutzt }

      { Selbst befreien }
      if fData <> nil then
         StrDispose(FData);

      inherited Destroy;

    except
      On E: Exception do
      begin
        if (not E.InheritsFrom(EInvalidPointer)) and (not E.InheritsFrom(EAccessViolation)) then
        begin
          SiMain.EnterMethod(lvDebug, Self, 'Destroy()', []);
            SiMain.LogException(E, E.Message);
          SiMain.LeaveMethod(lvDebug, Self, 'Destroy()');
        end;
      end;
    end;
  finally
    { finally free unneeded resources  }
  end;

end;



{*------------------------------------------------------------------------------

  Procedure   : getSize()
  Author      : Christian Ziegelt
  Date        : 28-Apr-2008

  @return ResultDescription
------------------------------------------------------------------------------*}

function TStrObj.getSize: Integer;
begin
  try
    try
      { ==== CODE HERE ====}
      Result := InstanceSize;
      { ===================}
    except
      On E: Exception do
      begin
        SiMain.EnterMethod(lvDebug, Self, 'getSize()', []);
          SiMain.LogException(E, E.Message);
        SiMain.LeaveMethod(lvDebug, Self, 'getSize()');
      end;
    end;
  finally
    { finally free unneeded resources  }
  end;

end;



{*------------------------------------------------------------------------------

  Procedure   : getData()
  Author      : Christian Ziegelt
  Date        : 18-Apr-2009

  @return ResultDescription
------------------------------------------------------------------------------*}

function TStrObj.getData: String;
begin
  try
    try

      Result := FData;

    except
      On E: Exception do
      begin
        SiMain.EnterMethod(lvDebug, Self, 'getData()', []);
          SiMain.LogException(E, E.Message);
        SiMain.LeaveMethod(lvDebug, Self, 'getData()');
      end;
    end;
  finally
    { finally free unneeded resources  }

  end;

end;




{*------------------------------------------------------------------------------

  Procedure   : setData()
  Author      : Christian Ziegelt
  Date        : 18-Apr-2009

  @param Value   ParameterDescription
  @return ResultDescription
------------------------------------------------------------------------------*}

procedure TStrObj.setData(const Value: String);
begin
  try
    try
      { alten String löschen }
      StrDispose(FData);

      { Platz reservieren für neuen String }
      FData := StrNew( pChar(Value));

    except
      On E: Exception do
      begin
        SiMain.EnterMethod(lvDebug, Self, 'setData()', []);
          SiMain.LogException(E, E.Message);
        SiMain.LeaveMethod(lvDebug, Self, 'setData()');
      end;
    end;
  finally
    { finally free unneeded resources  }

  end;

end;
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 06.07.09 13:12 
Moin und :welcome: im Forum!

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Nun gibt es aber Probleme mit dem String datentyp - ich möchte eine Datenstruktur verwenden, die ohne die optimierungen von Delphi funktioniert.
Warum? Grundsätzlich ist die Referenzzähler-Magic von Delphi eine sehr nützliche Sache (und ausserdem sehr schnell). :nixweiss:

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Vor Allem bleibt mir das Problem, aus dem Array hinterher wieder EFFIZIENT einen String zu erstellen - als Kopie. Das Objekt soll weiter unangetatstet bleiben.
Ich möchte hierbei nicht mit einer Schleife mystring := myString + Arr[i]; nutzen.
Die Hampelei mit der Typkonversion würde ich mir sparen. Ruf einfach einmal UniqueString(MyString); auf, wenn du einen Referenzzähler von 1 erzwingen willst - den Rest macht auch hier die Compiler-Magic intern (und auch recht schnell). :idea: ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
ChZiegelt Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 06.07.09 13:40 
Hallo, danke für das herzliche Willkommen !

Also, UniqueString() scheint einen Teil des Problems zu lösen, Danke für den Tipp.
Jetzt gibt aber Unique String den selben String zurück, wenn der RefCounter auf 1 steht.

Vom System her habe ich vermutlich Strings nie komplett verstanden.

In Thread T1 werden Daten ampfangen und aus dem Puffer in eine Stringvariable gelegt S1. S1 ist ein Member der Klasse, damit sie nicht auf dem Stack der empfangenden Routine angelegt wird.

Nun soll der Inhalt von S1 an alle Obserever von T1 gesendet werden, und zwar jeweils als Kopie. Dabei soll S1 unverändert (Inhalt und Adresse) in T1 erhalten bleiben.

Welche Methode zum Dublizieren von S1 sollte man hier nehmen ?
Um das Stack Problem zu umgehen habe ich hier eine Member Variable der T1 Klasse benutzt, P1 als Paket vom Typ TStrObj.
Wenn ich einen standart String verwendet habe, ist beim Empfänger regelmäßig Murks angekommen.
Das war mein eigentliches Problem.

Danke schonmal
Christian
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 06.07.09 13:41 
Und wenn man schnell mit einem String arbeiten will, dann kann man auch einfach diesen auf PChar casten und kann dann damit buchstabenweise arbeiten. Denn dies ist eigentlich das einzige, was an Strings langsam ist.

Wenn man natürlich dahergeht und immer ein Byte / einen Buchstaben zu einem Array / String hinzufügt, dann ist das sehr langsam. Das hat aber nichts mit Compilermagic zu tun, sondern einfach mit der eigenen äußerst ungünstigen Vorgehensweise...

Für das Versenden von Messages bietet sich PChar an. Es sollte eigentlich reichen den String darauf zu casten.

// EDIT:
Wenn der ursprüngliche String danach verändert werden soll, dann könntest du auch via GetMem Speicher für den PChar reservieren, die Daten hineinlegen, den verschicken und danach den Speicher wieder freigeben.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 06.07.09 14:27 
Moin!

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Jetzt gibt aber Unique String den selben String zurück, wenn der RefCounter auf 1 steht.
Und? FStringVar := UniqueString(FStringVar);, fertig, Referenzzähler ist auf 1. ;)

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
In Thread T1 werden Daten ampfangen und aus dem Puffer in eine Stringvariable gelegt S1. S1 ist ein Member der Klasse, damit sie nicht auf dem Stack der empfangenden Routine angelegt wird.
Dynamische Strings landen immer auf dem Heap (die Daten), lediglich der Zeiger darauf könnte auf dem Stack liegen. :idea:

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Nun soll der Inhalt von S1 an alle Obserever von T1 gesendet werden, und zwar jeweils als Kopie. Dabei soll S1 unverändert (Inhalt und Adresse) in T1 erhalten bleiben.

Welche Methode zum Dublizieren von S1 sollte man hier nehmen ?
Wenn du Objekte klonst, dann rufe einfach UniqueString dabei auf. Irgendwie verstehe ich nicht, wo du das Problem siehst. :gruebel: Wobei man die Daten eigentlich nicht kopieren muss, wenn man diese versendet (was genau ist bei dir damit gemeint?). Das sollte nur nötig sein, wenn die Daten (zwischendurch) verändert werden.

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich einen standart String verwendet habe, ist beim Empfänger regelmäßig Murks angekommen.
Das wird sicher nicht am "Standard-String" (was soll das eigentlich sein?) gelegen haben, sondern eher an nicht sauberer Threadsynchronisation (möglicherweise im Zusammenhang mit den referenzgezählten Strings - ich gebe zu, ich habe mir deinen Code nicht genauer angesehen :?). :nixweiss:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
ChZiegelt Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 06.07.09 16:23 
Danke nochmal für die Antworten.
Nun den Code reduziert auf das Wesentliche.

Zitat:
Und? FStringVar := UniqueString(FStringVar);, fertig, Referenzzähler ist auf 1.

Was ich hieran nicht verstehe, ist was tatsächlich passiert. Die Funktion hat keinen Rückgabewert.
Wenn ich nun 2 Strings habe, S1, und S2 - S1 weise ich einen Wert zu, S2 setz ich gleich S1. Dann sollte doch die Referenzzählung erhöht werden, und beide auf die selbe Adresse verweisen.
In einem Test passiert dies aber nicht. Dieser Code liefert das Ergebnis darunter:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  StackString: String;
  MyString: String;

begin
  StackString := edString.Text;
  memo.Lines.Add(Format('StackString:  %p: %s',[ Addr(StackString), StackString]));
  memo.Lines.Add(Format('MyString:     %p: %s',[ Addr(MyString), MyString]));
  MyString := StackString;
  memo.Lines.Add(Format('MyString:     %p: %s',[ Addr(MyString), MyString]));
  UniqueString(StackString);
  memo.Lines.Add(Format('StackString:  %p: %s',[ Addr(StackString), StackString]));


Ergebniss:
StackString: 0012F424: edString
MyString: 0012F420:
MyString: 0012F420: edString
StackString: 0012F424: edString

Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst.
Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig.

ausblenden 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:
procedure Klasse.BekommeDaten(Buffer: Pointer; Size: Cardinal);
var
  Str: String;
begin
  if Size > 0 then
  begin
    SetLength(Str, Size);
    CopyMemory(Pointer(Str), Buffer, Size);
    
  { Funktioniert nicht - Daten werden versendet, und die Procedure wird verlassen. 
    ab dann gibt es beim Observer während eines Zugriffs auf den Str eine Zugriffsverletzung }

    NotifyObserver( Str );

    (* Funktionierender Teil
      { String an die Observer Clients verteilen }
      StrObj := TStrObj.Create(Str);
      NotifyDataReceived(StrObj);
     *)



  end;

end;



Hier ist der Code wie ich ihn momentan verwende, ich weiss halt nur nicht ob das so funktionieren kann oder ob ich noch einem gedanklichen Kurzschluss unterliege:

ausblenden volle Höhe 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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
{ TStrObj }

function TStrObj.Clone: TMsgDataType;
begin
  Result := TStrObj.Create(Self.Data);

end;

constructor TStrObj.Create(const Data: String);
var
  len: Integer;
begin
  inherited Create;

   { Bei dieser Zuweiseung wird schon eine Kopie angelegt }
    Self.Data := Data;

end;

destructor TStrObj.Destroy;
begin
  { Selbst befreien }
  if fData <> nil then
     StrDispose(FData);

  inherited Destroy;

end;

function TStrObj.getData: String;
begin
  Result := FData;
end;

procedure TStrObj.setData(const Value: String);
begin
  { alten String löschen }
    StrDispose(FData);

  { Platz reservieren für neuen String }
    FData := StrNew( pChar(Value));
end;
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 06.07.09 16:27 
user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst.
Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig.
Das ist ja auch richtig so. Lokale Variablen sind eben lokale Variablen.

Reserviere eben mit GetMem den Speicher und benutze PChar, dann kannst du damit machen was du willst. Du darfst nur nicht vergessen den auch wieder freizugeben. PChars haben keine Referenzzählung und mit GetMem reservierter Speicher wird auch nicht automatisch wieder freigegeben.
ChZiegelt Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 06.07.09 16:38 
Gut, versuche ich es über diesen Weg,
Danke für die Antworten !!

Und noch einen schönen Abend
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mo 06.07.09 19:45 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Und wenn man schnell mit einem String arbeiten will, dann kann man auch einfach diesen auf PChar casten und kann dann damit buchstabenweise arbeiten. Denn dies ist eigentlich das einzige, was an Strings langsam ist.

Nein, da muss ich widersprechen. Manchmal ist PChar schneller, manchmal die Indizierung des Strings. Viel nehmen sie sich nicht, obwohl es wahrscheinlicher ist, mit PChar ein schnelleres Verfahren zu implementieren.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Wenn man natürlich dahergeht und immer ein Byte / einen Buchstaben zu einem Array / String hinzufügt, dann ist das sehr langsam. Das hat aber nichts mit Compilermagic zu tun, sondern einfach mit der eigenen äußerst ungünstigen Vorgehensweise...
Auch nein, denn erstens hat es etwas mit Compilermagic zu tun und zweitens werkelt mittlerweile der optimierte Memorymanager von FastMM in Delphi und der sorgt dafür, das fast kein Unterschied zwischen der zeichenweisen Konkatenation und dem einmaligen Reservieren und anschließende Befüllen besteht.
Hier eine sehr interessante Diskussion und ein Beispielprojekt: www.delphipraxis.net...ighlight=performance

Dessenungeachtet verstehe ich die Problematik hier nicht. Ebensowenig wie das Versenden eines Strings per Windows-Message. Wenn ich ein Observer-Pattern implementiere, kann ich den String doch einfach als 'Const' an die einzelnen Observer reichen. Wer eine Kopie anlegen möchte, kann das tun (per einfacher Zuweisung). Wer das nicht will, lässt es. Ist das nicht optimal, oder hab ich was verpasst?

_________________
Na denn, dann. Bis dann, denn.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 06.07.09 21:53 
Also das gehört zwar nicht direkt zum Thema, aber das muss ich dann doch noch dazu schreiben...

user profile iconalzaimar hat folgendes geschrieben Zum zitierten Posting springen:
und zweitens werkelt mittlerweile der optimierte Memorymanager von FastMM in Delphi und der sorgt dafür, das fast kein Unterschied zwischen der zeichenweisen Konkatenation und dem einmaligen Reservieren und anschließende Befüllen besteht.
Hier eine sehr interessante Diskussion und ein Beispielprojekt: www.delphipraxis.net...ighlight=performance
Da merkt man mal wieder den Unterschied zwischen irgendwelchen Beispielen und realen Projekten. Wenn ich das Auslesen aus einer .reg Datei (Unicode) bei meinem Registryeditor durch Stringkonkatenation mache, dauert es ca. 19,8 Sekunden für den gesamten Zweig HKLM. Wenn ich das selbe mit SetString + Pointern mache, dauert es ca. 9,1 Sekunden... :roll:

Und ich habe wirklich nur die paar Zeilen verändert.
Alt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
    if (FBufferPos = 0and (FPosInBuffer = 0then
    begin
      Inc(PByte(StartPointer), 2);
      SetString(AValue, nil, LineLen div 2 - 1);
    end
    else
      SetString(AValue, nil, LineLen div 2);
    if Length(AValue) > 0 then
    begin
      CurValue := PByte(AValue);
      while Integer(StartPointer) < Integer(CurData) do
      begin
        CurValue^ := PByte(StartPointer)^;
        Inc(PByte(StartPointer), 2);
        Inc(CurValue);
      end;
    end;
"Neu":
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
    if (FBufferPos = 0and (FPosInBuffer = 0then
      Inc(PByte(StartPointer), 2);
    AValue := '';
    while Integer(StartPointer) < Integer(CurData) do
    begin
      AValue := AValue + PChar(StartPointer)^;
      Inc(PByte(StartPointer), 2);
    end;
Du kannst es gerne selbst in der Unit FileAccessUtils.pas des Projekts ausprobieren...
ChZiegelt Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 06.07.09 23:53 
Ok, ich hatte zwar nicht mit weiteren Meldungen gerechnet, aber trotzdem danke :-)

Zitat:

Dessenungeachtet verstehe ich die Problematik hier nicht. Ebensowenig wie das Versenden eines Strings per Windows-Message. Wenn ich ein Observer-Pattern implementiere, kann ich den String doch einfach als 'Const' an die einzelnen Observer reichen. Wer eine Kopie anlegen möchte, kann das tun (per einfacher Zuweisung). Wer das nicht will, lässt es. Ist das nicht optimal, oder hab ich was verpasst?


Die Problematik war halt, dass es für mich nicht ersichtlich war, wann ein String tatsächlich dubliziert wird, und wann er mit neuer Referenz versehen wird.
Ursprünglich senden 2 bis 5 Komunikations Threads empfangene Daten an Ihre Observer. Diese sammeln die Strings so lange bis ein gültiges Telegramm vorliegt, und bearbeiten es weiter.

Ich nutze keine Methode mit const Übergabe, um keine Locks der Threads zu erzeugen - kann mir also die Synchronisierung sparen.
Um die Gefahr zu vermeiden, dass die PostMessage() nicht empfangen wird, oder verloren geht, werden alle versendeten Strings vom Versender in eine Liste eingetragen (deswegen das Objekt um den String herum, aber auch aus anderen Gründen wie Timestamp, Clone,…), um später ab einer bestimmten Zeit befreit zu werden.

Nun ergab es sich das eine oder andere MAl dass Zugriffsverletzungen auftraten, weil Strings nicht mehr zugewiesen waren (die ContainerObjekte aber durchaus).

Ich wollte vor Allem von jemadem der sich damit auskennt wissen, ob der Code soweit er dort steht funktioniert, oder ob ich auf Alternativen ausweichen sollte.

Da ich Strings im Detail nicht verstanden zu haben scheine, dachte ich könnte ich das Problem mit einem Char Array oder ähnlichem lösen, da wüsste ich wann ich wo speicher reserviere und wann ich ihn wieder freigeben muss.

Hierzu war meine Frage ob das effizient lösbar ist.

effizient heisst bei mir, jeder Com Thread bekommt etwa alle 20ms bis 250ms einen String rein, schickt den weiter an beliebig viele Observer und ist wieder empfangsbereit.

Gruß
Christian
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 07.07.09 00:08 
Moin!

So, jetzt nochmal in Ruhe drübergeschaut (war heute tagsüber leider nicht drin). ;)
user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Die Funktion hat keinen Rückgabewert.
Ja, das ist richtig ;) ich hatte keine Zeit, in die Doku zu schauen. :oops:

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Was ich hieran nicht verstehe, ist was tatsächlich passiert.
Das liegt vermutlich daran, dass dein Test-Beispiel nicht korrekt ist: Addr() liefert die Adresse der Variablen, nicht die Adresse, auf die eine Zeigervariable zeigt (=Inhalt). Das hier wäre korrekt und ist auch gleich um den Referenzzähler erweitert:
ausblenden 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:
var
  Form1: TForm1;
  MyString: String;

procedure TForm1.Button1Click(Sender: TObject);
  var
    StackString: String;

  function GetRefCount(const AStr: String): Cardinal;
  begin
    if (AStr <> ''then
      Result := PCardinal(Integer(Pointer(AStr))-8)^
    else
      Result := 0;
  end;

begin
  StackString := edString.Text;
  Memo.Lines.Add(Format('StackString:  %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString]));
  Memo.Lines.Add(Format('MyString:     %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString]));
  MyString := StackString;
  Memo.Lines.Add('MyString := StackString;');
  Memo.Lines.Add(Format('StackString:  %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString]));
  Memo.Lines.Add(Format('MyString:     %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString]));
  UniqueString(StackString);
  Memo.Lines.Add('UniqueString(StackString);');
  Memo.Lines.Add(Format('StackString:  %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString]));
  Memo.Lines.Add(Format('MyString:     %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString]));
end;
Liefert folgende Ausgabe:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
StackString:  00982550[1]: edString
MyString:     00000000[0]: 
MyString := StackString;
StackString:  00982550[2]: edString
MyString:     00982550[2]: edString
UniqueString(StackString);
StackString:  009843C4[1]: edString
MyString:     00982550[1]: edString

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst.
Strings, die mit "beliebiger" Länge auf dem Heap verwaltet werden.

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig.
Das ist klar, hat user profile iconjaenicke ja auch schon erläutert.

Gib einfach die Referenz auf den String weiter (durch eine einfache Zuweisung), dann kannst du auch sogar die lokale Variable für den Empfang beibehalten:
user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure Klasse.BekommeDaten(Buffer: Pointer; Size: Cardinal);
var
  Str: String;
begin
  if Size > 0 then
  begin
    SetLength(Str, Size);
    CopyMemory(Pointer(Str), Buffer, Size);
    
    NotifyObserver( Str ); // Hier im Observer die Referenz durch Zuweisung auf eine eigene Variable aufheben,
                           // wird der String nicht mehr benötigt, einfach auf NIL oder leeren String setzen.
                           // Wenn du den String veränderst, macht Delphi automatisch eine Kopie davon.
                           // Du kannst natürlich auch vorher sicherheitshalber UniqueString() aufrufen.
  end;
end;
Das sollte im Wesentlichen user profile iconalzaimars Vorschlag entsprechen.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Reserviere eben mit GetMem den Speicher und benutze PChar, dann kannst du damit machen was du willst. Du darfst nur nicht vergessen den auch wieder freizugeben. PChars haben keine Referenzzählung und mit GetMem reservierter Speicher wird auch nicht automatisch wieder freigegeben.
Das ist in einer multithreaded Umgebung kein wirklich guter Ansatz, da hier die Referenzzählung an dir kleben bleibt - das macht aber Delphi doch gerne für dich. ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Di 07.07.09 07:44 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Da merkt man mal wieder den Unterschied zwischen irgendwelchen Beispielen und realen Projekten. ...
Das war nicht 'irgendein Beispiel', lieber Sebastian, sondern die Extraktion aus einem konkreten Projekt. Da sieht man mal wieder den Unterschied zwischen Lesen und Verstehen. :roll:

_________________
Na denn, dann. Bis dann, denn.
ChZiegelt Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Di 07.07.09 16:30 
Na Ihr beide mögt Euch ja ;-)
Wegen mir braucht Ihr Euch aber nicht zu zerfleichen…

Noch einmal zurück zu dem Thema:
So langsam denke ich habe ichs mit Strings, allerdings komme ich um das Container Objekt nicht herum.

Sehe ich das richtig, dass ich simpel ein Container Objekt (StrObj) anlege und den Str aus der Datenempfangsroutine dem Objekt zuweise ?

ausblenden Delphi-Quelltext
1:
StrObj.Data = Str;					

Und im Zweifel noch die UniqueString() Routine darauf ausführe ?

Gruß
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 07.07.09 16:48 
Moin!

user profile iconChZiegelt hat folgendes geschrieben Zum zitierten Posting springen:
Sehe ich das richtig, dass ich simpel ein Container Objekt (StrObj) anlege und den Str aus der Datenempfangsroutine dem Objekt zuweise ?

ausblenden Delphi-Quelltext
1:
StrObj.Data := Str;					

Und im Zweifel noch die UniqueString() Routine darauf ausführe ?
Ja, würde ich so sehen. Lass das UniqueString() erstmal weg, solange du in den Threads nur die Strings liest, sollte das wunderbar klappen. ;) Du brauchst dich auch nicht um die Freigabe der Strings kümmern, gib einfach den Container frei, die CompilerMagic sollte den String/Referenz sauber wegräumen.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.