Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Kurze Frage zur Thread Sicherheit (https://www.delphipraxis.net/170806-kurze-frage-zur-thread-sicherheit.html)

luke2 4. Okt 2012 00:50

Delphi-Version: XE2

Kurze Frage zur Thread Sicherheit
 
Hi, ist es sicher, wenn ein Thread lesend und schreibend auf ein Index eines Array zugreift und ein anderer Thread lesend und schreibend auf das gleiche Array mit einem anderen Index zugreift? Oder müssen alle Zugriffe synchronisiert werden?

Zacherl 4. Okt 2012 01:26

AW: Kurze Frage zur Thread Sicherheit
 
Sollte theoretisch sicher sein, solange wirklich immer nur ein Thread auf einen Index gleichzeitig zugreift.

luke2 4. Okt 2012 02:04

AW: Kurze Frage zur Thread Sicherheit
 
OK, danke für die Antwort.

himitsu 4. Okt 2012 08:24

AW: Kurze Frage zur Thread Sicherheit
 
Und sich das Array selber nicht verändert, also kein SetLength und Co.

Was ist das denn für ein Array?

geskill 4. Okt 2012 11:04

AW: Kurze Frage zur Thread Sicherheit
 
Hallo luke2,
du kannst dafür am besten einen MREW benutzen. Damit können mehrere Threads gleichzeitig lesend drauf zugreifen, aber immer nur einer schreibend. Natürlich müssen beide Threads die gleiche Instanz von dem TSafeArray bekommen ;)

Bei so kritischen Sachen würde ich nie wie du vorhast die Array Instanz einem Thread direkt mitteilen.

Dies ist eine vereinfachtere Variante die ich gerade im Einsatz habe:
Das ist an sich eigentlich schon ziemlich performant, aber du könntest jetzt ein lokales Array/Liste etc. anlegen, die alle Lesezugriffe auf die versch. Indexe speichert. Und beim Schreiben wird dann geprüft ob dieser Index in der Liste ist, wenn ja muss dort beim zugreifen eben gewartet werden. Aber ich würde das mit dem gleichzeitigen auf ein Index auch sein lassen und immer als schreibend behandeln.
Delphi-Quellcode:
uses
  // Delphi
  Math,
  // OmniThreadLibrary
  OtlSync;

type
  TSafeArray = class
  private
    FArray: array of Integer;
    FLock: TOmniMREW;
  public
    constructor Create;

    procedure SetResult(AIndex: Integer; const AValue: Integer);
    function GetResult(AIndex: Integer): Integer;
  end;

constructor TSafeArray.Create;
begin
  SetLength(FArray, 0);
end;

procedure TSafeArray.SetResult(AIndex: Integer; const AValue: Integer);
begin
  FLock.EnterWriteLock;
  try
    SetLength(FArray, Max(AIndex, length(FArray)) + 1); // Lesezugriff: length(); Schreibzugriff!: SetLength()
    FArray[AIndex] := AValue; // Schreibzugriff: FArray[Index]
  finally
    FLock.ExitWriteLock;
  end;
end;

function TSafeArray.GetResult(AIndex: Integer): Integer;
var
  Index: Integer;
begin
  Result := -1;

  Index := AIndex;

  FLock.EnterReadLock; // starte das lesen
  try
    if Index < length(FArray) then // Lesezugriff: length()
      Result := FArray[Index]; // Lesezugriff: FArray[Index]
  finally
    FLock.ExitReadLock; // beende das lesen
  end;
end;
Grüße

luke2 4. Okt 2012 15:31

AW: Kurze Frage zur Thread Sicherheit
 
Danke, das sieht gut aus, geskill, aber ich möchte eigentlich keine externen Komponenten benutzen.

Es sieht ungefähr so aus:
Delphi-Quellcode:
type
  T = record
    Str: string;
    Use: Boolean;
  end;

private
  A: Array[0..5] of T;

procedure TThread.Add(const S: string);
begin
  with A[GetFreeField] do
  begin
    Str := S;
    Use := True;
  end;
end;

procedure TThread.Execute;
begin
  while not Terminated do
  begin
    for I := Low(A) to High(A) do
      if Use then
      begin
        //hier werden nur Felder von A[I] gelesen und geändert
      end;
    Sleep(1);
  end;
end;
Add wird aus dem Hauptthread aufgerufen. Use ist natürlich anfangs immer False und wird vom Thread auch irgendwann wieder auf False gesetzt. Das sollte doch sicher sein?

geskill 4. Okt 2012 15:57

AW: Kurze Frage zur Thread Sicherheit
 
Also wenn du Add() aufrufst bevor der Thread startet, dann geht das in Ordnung.

Nur so musst du das immer wissen, wann du es aufrufen darfst. Bei kleinen Programmen ist das noch in Ordnung, aber man sollte sich besser direkt angewöhnen das sauber umzusetzen. In dem Beispiel bei mir kann man dies ohne zu überlegen einfach aufrufen.

Das Auslesen passiert dann auch erst wieder wenn der Thread definitiv beendet wurde (z.B. via Join)?

Da es ein statisches Array ist sind die Schreibtugriffe ja wirklich nur aus die einzelnen Elemente begrenzt.

Und mit 2 Threads meinst du Hauptthread und TThread - oder? ;)

luke2 4. Okt 2012 16:04

AW: Kurze Frage zur Thread Sicherheit
 
Zitat:

Zitat von geskill (Beitrag 1185738)
Also wenn du Add() aufrufst bevor der Thread startet, dann geht das in Ordnung.

Add wird eigentlich nur aufgerufen, wenn der Thread bereits läuft. Was kann da denn passieren? Bisher lief die Anwendung eigentlich immer stabil.
Zitat:

Zitat von geskill (Beitrag 1185738)
Und mit 2 Threads meinst du Hauptthread und TThread - oder? ;)

Jep

Kann man dein Beispiel auch mit den Windows CriticalSections (TRTLCriticalSection) umsetzen, so dass keine externe Unit/Komponente benutzt werden muss?

geskill 4. Okt 2012 16:32

AW: Kurze Frage zur Thread Sicherheit
 
Die Schleife ist ja kurz und bei den heutigen leistungsstarken PC gehört schon ein bisschen Glück dazu das ein Fehler Auftritt. Weil beim Add() schreibst du ja auf einen Index I und bei jedem lesen greifst du immer auf Used von I lesend zu. Das ist nicht in Ordnung.

Mit einer TCriticalSection kannst du das nun einschränken. Der Vorteil bei den OTL Sections ist das man sie nicht initialisieren und freigeben muss. Zudem ist der MREW auch "sicherer" als der von Delphi.

luke2 4. Okt 2012 16:41

AW: Kurze Frage zur Thread Sicherheit
 
OK, vielen Dank. Eine letzte Frage: Ist es denn so sicher (auch wenn es keine optimale Lösung ist)?

Delphi-Quellcode:
type
  T = record
    Str: string;
    Use: Boolean;
  end;

private
  A: Array[0..5] of T;
  CS: TRTLCriticalSection;

procedure TThread.Add(const S: string);
begin
  EnterCriticalSection(CS);
  try
    with A[GetFreeField] do
    begin
      Str := S;
      Use := True;
    end;
  finally
    LeaveCriticalSection(CS);
  end;
end;

procedure TThread.Execute;
begin
  while not Terminated do
  begin
    for I := Low(A) to High(A) do
    begin
      EnterCriticalSection(CS);
      if Use then
      begin
        LeaveCriticalSection(CS);
        //hier werden nur Felder von A[I] gelesen und geändert
      end
      else
        LeaveCriticalSection(CS);
    end;
    Sleep(1);
  end;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:34 Uhr.
Seite 1 von 2  1 2      

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