AGB  ·  Datenschutz  ·  Impressum  







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

Ist SetLength Thread-safe?

Ein Thema von iphi · begonnen am 24. Okt 2009 · letzter Beitrag vom 25. Okt 2009
Antwort Antwort
Seite 1 von 2  1 2      
iphi

Registriert seit: 13. Feb 2009
262 Beiträge
 
Delphi 7 Personal
 
#1

Ist SetLength Thread-safe?

  Alt 24. Okt 2009, 15:56
Hallo,

ich habe einen Thread, der Daten sammelt und diese in ein globales dynamisches Array ablegen soll.
Ist es ok, wenn der Thread das array mit setlength vergrößert oder kann es darurch z.B. mit dem VCL Hauptthread zu Speicherkonflikten kommen?

Gruß, Thomas
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#2

Re: Ist SetLength Thread-safe?

  Alt 24. Okt 2009, 16:14
Da stellt sich mal wieder die Frage: Was bedeutet eigentlich threadsicher?
Die von dir gewünschte Antwort dürfte allerdings nein lauten. Es kann dir nämlich passieren, dass der Hauptthread einen Zeiger dereferenziert, den der zweite Thread schon dem Speichermanager als frei gemeldet hat.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.088 Beiträge
 
Delphi 12 Athens
 
#3

Re: Ist SetLength Thread-safe?

  Alt 24. Okt 2009, 17:25
Also ganz einfach alle Zugriffe auf dieses Array über eine CriticalSection oder Ähnliches absichern.

Ganz Einfach, alles, was mit Pointer arbeitet und sich nicht als Delphi-Referenz durchsuchenthreadvar deklarieren läßt, das mußt du absichern.

Von Natur aus sind erstmal nur die Referenzzählungen von Strings (AnsiString/UnicodeString) und Interfaces ThreadSave, weil Delphi dort selber schon threadsichere Zugriffe nutzt ... um alles andere mußt du dich selber kümmern.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
iphi

Registriert seit: 13. Feb 2009
262 Beiträge
 
Delphi 7 Personal
 
#4

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 08:50
Danke für das Feedback.

Zitat:
Es kann dir nämlich passieren, dass der Hauptthread einen Zeiger dereferenziert, den der zweite Thread schon dem Speichermanager als frei gemeldet hat.
Jetzt frage ich mich allerdings, wie ichs richtig mache.

Ich möchte folgendes erreichen:
Der VCL-Hauptthread übergibt dem Sammlerthread z.B. in SammlerThread.Create ein leeres dynamisches Array, genauer einen Zeiger auf ein globales solches. Der Sammlerthread sammelt Daten, vergrößert das globale Array nach Bedarf und schreibt die gesammelten Daten blockweise hinein.
Der VCL Hauptthread sieht an der Länge des dynamischen Arrays immer, wie viele Daten schon gesammelt sind.
Derselbe Sammlerthreadcode soll in verschiedenen Instanzen in verschiedene Arrays sammeln, daher ist die Übergabe des Arraypointers erforderlich.

Ist das so machbar und wenn ja, mit welcher Konstruktion?

Gruß, Thomas
  Mit Zitat antworten Zitat
Benutzerbild von wicht
wicht

Registriert seit: 15. Jan 2006
Ort: Das schöne Enger nahe Bielefeld
809 Beiträge
 
Delphi XE Professional
 
#5

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 10:03
Guten Morgen!
Hier mal ein kleines Beispiel, so zusammengetippt und ohne Anspruch auf irgendetwas, aber es sollte die Benutzung von CriticalSections zeigen, ausserdem gibt es noch ein Event, um der Form bescheid zu sagen. Hoffe, hier sind nicht allzuviele Böcke drin ..

Delphi-Quellcode:
interface

type
  TMyArray = array of string;

  TDataThread = class(TThread)
  private
    FOnNotify: TNotifyEvent;
    FArray: TMyArray;

    procedure DoNotify;
  protected
    procedure Execute; override;
  public
    constructor Create(Arr: TArray);
    property OnNotify: TNotifyEvent read FOnNotify write FOnNotify;
  end;

  TMyForm = class(TForm)
  private
    ThreadA: TDataThread;
    ThreadB: TDataThread;
    procedure ThreadNotify(Sender: TObject);
  public
    constructor Create(AOwner: TComponent);
  end;

var
  CS: TCricicalSection;

implementation

constructor TDataThread.Create(Arr: TMyArray);
begin
  inherited Create(True);
  FArray := Arr;
end;

procedure TDataThread.Execute;
var
  i: Integer;
  FTempArray: TArray;
begin
  SetLength(FTempArray, 0);

  while True do
  begin
    // Mache ganz viel. Befülle das FTempArray.
    // Wenn FTempArray "voll" ist, wird
    // die CS betreten, das echte Array manipuliert,
    // Der Form bescheid gesagt, und dann geht es weiter.

    // Einen gesammelten Datensatz dem internen Array hinzufügen
    SetLength(FTempArray, Length(FTempArray) + 1);
    FTempArray[High(FTempArray)] := 'asdf';

    if Length(FTempArray > 100)
    begin
      CS.Enter; // Zugriff global absichern
      SetLength(FArray, Length(FArray) + Length(FTempArray));
      for i := 0 to Length(FTempArray) do // kA ob Schleife so passt, ist noch zu früh.
        FArray[Length(FArray) - Length(FTempArray) + i] := FTempArray[i];
      CS.Leave; // Und Zugriff wieder erlauben
      Synchronize(DoNotify); // Und der Form bescheid sagen, dass neue Daten da sind
    end;
    SetLength(FTempArray, 0);
  end;
end;

procedure TDataThread.DoNotify;
begin
  if Assigned(FOnNotify)
    FOnNotify(Self);
end;

constructor TMyForm.Create(AOwner: TComponent);
begin
  inherited;
  SetLength(FArray, 0);
  CS := TCriticalSection.Create;
  ThreadA := TDataThread.Create(FArray);
  ThreadA.OnNotify := ThreadNotify;
  ThreadA.Resume;

  ThreadB := TDataThread.Create(FArray);
  ThreadB.OnNotify := ThreadNotify;
  ThreadB.Resume;
end;

procedure TMyForm.ThreadNotify(Sender: TObject);
begin
  // Thread hat Datenblock gesammelt, hier könnte
  // man jetzt auf das Array zugreifen, ganz bequem.
  // Wichtig ist hier auch die CriticalSection und
  // man sollte zusehen, dass das, was man hier macht,
  // ganz schnell geht, weil ein Thread, der sein
  // internes Array voll hat und die Daten übertragen
  // möchte das bis zum CS.Leave nicht kann.
  CS.Enter;
  ShowMessage(FArray[0]);
  CS.Leave;
end;
http://streamwriter.org

"I make hits. Not the public. I tell the DJ’s what to play. Understand?"
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.088 Beiträge
 
Delphi 12 Athens
 
#6

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 11:46
Zitat von iphi:
daher ist die Übergabe des Arraypointers erforderlich.
am Besten wäre es, wenn du das Array in einer Klasse verwaltest und Zugriffe immer nur über Funktionen/Properties dieser Klasse (das kann auch der Hauptthread, bzw. das Formular sein) wird auf die Daten des Arrays zugegriffen.
Dann hast du auch die Synchronisierung (CriticalSection) zentral an einer Stelle.

Ansonsten mußt du ja die CriticalSection und einen Zeiger auf die Array-Variable übergeben und nicht auf das Array selber.
Dann kann sich jeder selber den Weg zum Array dereferenzieren und kann dieses auch bearbeiten.

Oder man könnte noch über Synchronize auf das Array im Haupthtread/Formular zugreifen.

[edit]
@wicht: TCricicalSection kommt natürlich auch dahin, wo das Array liegt
Stell dir mal vor man hat von den Klassen mehere, dann bremsen ihre Zugriffe sich gegenseitig aus.
Und wenn jemand TMyForm mehrfach erstellt, dann drehen die Sections durch, weil sie überschrieben und auch mal vorzeitig freigegeben werden (also die CS, welche als letztes in der Variable stand wird weg sein, wenn man ein solches Formular wieder freigibt ... das ist auch der Grund, warum globale Variablen meißtens böse sind ).
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
Benutzerbild von wicht
wicht

Registriert seit: 15. Jan 2006
Ort: Das schöne Enger nahe Bielefeld
809 Beiträge
 
Delphi XE Professional
 
#7

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 13:19
Ist ganz klar. War nur als kleines Beispiel gemeint, aber man sollte es schon noch überarbeiten. Die Verwendung einer CS und vom Synchronize wird jedenfalls demonstriert, mehr wollte ich nicht.
So früh am Morgen Premiumcode schreiben, dafür bin ich schon zu alt..
http://streamwriter.org

"I make hits. Not the public. I tell the DJ’s what to play. Understand?"
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#8

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 13:44
Hier mal als Beispiel für die von himi angesprochene Klasse (für einen string mit speziellen Aufgaben)
Delphi-Quellcode:
TSyncString=class(TSimpleRWSync)
  private
    FValue:String;
    procedure setValue(Value:string);
    function getValue:string;
  public
    function length:Integer;
    property Value:string read getValue write SetValue;
    procedure Add(const Value:string); //string um einen weiteren ergänzen
    procedure Delete(Pos,Count:Integer); //ein teil des strings löschen
  end;


{ TSyncString }

procedure TSyncString.Add(const Value: string);
begin
  BeginWrite;
  try
    FValue:=FValue+Value;
  finally
    EndWrite;
  end;
end;

procedure TSyncString.Delete(Pos, Count: Integer);
begin
  BeginWrite;
  try
    system.Delete(FValue,Pos,Count);
  finally
    EndWrite;
  end;
end;

function TSyncString.getValue: string;
begin
  BeginRead;
  try
    result:=FValue;
  finally
    EndRead;
  end;
end;

function TSyncString.length: Integer;
begin
  BeginRead;
  try
    result:=system.length(FValue);
  finally
    EndRead;
  end;
end;

procedure TSyncString.setValue(Value: string);
begin
  BeginWrite;
  try
    FValue:=Value;
  finally
    EndWrite;
  end;
end;
TSimpleRWSync hat so eine CS. Wenn man noch mehrere Threads zugreifen lässt, dann ist es sinnvoll lesende von schreibenden Zugriffen zu "trennen", das macht dann TMultiSync..irgendwas

Auch ist es sinnvoll sich TThreadlist anzusehen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
iphi

Registriert seit: 13. Feb 2009
262 Beiträge
 
Delphi 7 Personal
 
#9

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 14:21
Erstmal vielen Dank für Euer umfangreiches Feedback.
Für mich als Threadinganfänger ist das noch ein bisschen schwierig nachzuvollziehen.

Hier erst mal ein paar Fragen für mein Verständnis:

1. Nach meinem Verständnis sind nur Schreibzugriffe "böse", die sich potentiell mit anderen Schreib- oder Lesezugriffen überschneiden können. Wenn nur gelesen wird, sind nach meinem Verständnis critical sections unnötig. Richtig?

2. Nach meinem Verständnis sind critical sections auch bei Schreibzugriffen unnötig, wenn andersweitig sichergestellt ist, dass kein anderer Code auf dieselbe Resource gleichzeitig zugreift. Richtig?

3. Kann es sein, dass es die Klassen TSimpleRWSync bzw. TMultiSync in Delphi6 (Personal) nicht gibt?

Wenn meine Denke soweit stimmt, dann hätte ich mir folgende Strategie zurechtgelegt:

Ich hab ein globales dynamisches Array x von dynamischen Arrays. Wenn neue Daten anliegen, vergrößert der Sammlerthread x um ein Arrayelement, welches seinerseits ja ein dynamisches Array ist, setzt letzteres auf die notwendige Länge und schreibt die neuen Daten da rein. Über einen globalen "Füllstandsanzeiger" könnte man gewährleisten, dass niemand lesend auf das noch nicht aktualisierte Element (=Subarray) von x zugreift, so lange der Sammlerthread noch nicht fertig ist mit schreiben. Auf alle anderen Elemente (=Subarrays) von x müsste man problemlos zugreifen können, da die nicht mehr verändert werden.
Dann müsste nur der Zugriff auf den Füllstandszeiger in eine critical section (und setlength-Anweisungen natürlich).

Macht das Sinn?

Gruß,
Thomas
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#10

Re: Ist SetLength Thread-safe?

  Alt 25. Okt 2009, 14:45
Du musst dir bewusst machen, was bei SetLength alles geschehen kann.
Nehmen wir an, du hast ein Array mit 100 Elementen. Es liegt als Datenstruktur im Heap. Wenn du es nun auf tausend Elemente vergrößern willst, wird, wenn du Glück hast, noch Speicher nach dem alten Array frei sein, der dann dem Array hinzugefügt wird. Wenn kein Speicher frei ist, muss das Array verschoben werden. Und genau dann ist deine Annahme verletzt.
Zitat:
Auf alle anderen Elemente von x müsste man problemlos zugreifen können, da die nicht mehr verändert werden.
Wenn du Pech hast, holt sich der Lese-Thread nämlich den alten Arrayzeiger, und bevor er ihn dereferenzieren kann, wird das Array von einem schreibenden Thread verschoben. Wenn du Glück hast, raucht dir der Lese-Thread mit einer Zugriffsverletzung ab - andernfalls kannst du dir leicht den Heap zerschießen.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 01:50 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