Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Synchronisierung zwischen Threads ohne Mutex (https://www.delphipraxis.net/216613-synchronisierung-zwischen-threads-ohne-mutex.html)

AJ_Oldendorf 28. Jan 2025 10:21

Synchronisierung zwischen Threads ohne Mutex
 
Hallo zusammen,
ich habe eine Frage, wie man folgendes anders lösen kann:

globale Liste

Task 1 (Thread):
zyklischer Check der kompletten Liste und irgendwelche Aktionen damit machen

Task 2 (Thread):
Löschen und Hinzufügen von Einträgen in der globalen Liste

Es geht hier NICHT um VCL und Synchronize.
Das ganze kann man mit einer Mutex machen und bevor jemand mit der globalen Liste etwas machen möchte, wird sich die Mutex gezogen und nur wenn man diese bekommt (also kein anderer Besitzer vorhanden ist), hat man den Zugriff auf die globale Liste. Das funktioniert und darauf möchte ich auch nicht weiter eingehen.
Ich möchte nur wissen, welche Mechanismen es noch gibt, eine derartige Synchronisation zwischen den Threads zu machen.
Natürlich erledigen die Threads noch andere Aufgaben, dass ist nur eine Teilbeschreibung.

Zum allgemeinen Hintergrund warum das Ganze:
Wenn Task 1 gerade in einer Schleife über alle Items ist und Task 2 gerade Items löscht oder hinzufügt, dann knallt es in Task 1

DeddyH 28. Jan 2025 10:37

AW: Synchronisierung zwischen Threads ohne Mutex
 
https://docwiki.embarcadero.com/Libr...ystem.SyncObjs, da kannst Du Dir das Passende aussuchen, System.TMonitor sollte IMO aber auch funktionieren.

jaenicke 28. Jan 2025 12:14

AW: Synchronisierung zwischen Threads ohne Mutex
 
Wenn es um einfache Benutzerbarkeit geht, gibt es System.Generics.Collections.TThreadList<T>, die das Locking intern über TMonitor regelt.

Wie oft passieren denn Lese- und Schreibzugriffe? Wenn Schreibzugriffe im Vergleich deutlich seltener als Lesezugriffe sind (die ja nicht gegenseitig synchronisiert werden müssen, solange kein Schreibzugriff passiert), gibt es System.SyncObjs.TLightweightMREW, das für diesen Fall sehr performant ist. Das sorgt dafür, dass jeder lesen kann wie er möchte, aber ein Schreibzugriff nur exklusiv passiert.

Ich habe die Formulierung aufgrund von Peters Hinweis angepasst. Und natürlich muss man bei TLightweightMREW auch einen logischen Lesezugriff komplett absichern. Wenn der also in einem Block mit einer Schleife passiert, muss man den Block insgesamt als Lesezugriff absichern.

peterbelow 28. Jan 2025 12:53

AW: Synchronisierung zwischen Threads ohne Mutex
 
Zitat:

Zitat von jaenicke (Beitrag 1545701)
Wenn es um einfache Benutzerbarkeit geht, gibt es System.Generics.Collections.TThreadList<T>, die das Locking intern über TMonitor regelt.

Wie oft passieren denn Lese- und Schreibzugriffe? Wenn Schreibzugriffe im Vergleich deutlich seltener als Lesezugriffe sind (die ja nicht synchronisiert werden müssen), gibt es System.SyncObjs.TLightweightMREW, das für diesen Fall sehr performant ist.

Vorsicht! Lesezugriffe auf eine Liste müssen durchaus synchronisiert werden, wenn ein anderer Thread die Zahl oder Reihenfolge der Items ändern kann, besonders, wenn das Lesen in einer Schleife erfolgt.

jaenicke 28. Jan 2025 13:48

AW: Synchronisierung zwischen Threads ohne Mutex
 
Zitat:

Zitat von peterbelow (Beitrag 1545703)
Vorsicht! Lesezugriffe auf eine Liste müssen durchaus synchronisiert werden, wenn ein anderer Thread die Zahl oder Reihenfolge der Items ändern kann, besonders, wenn das Lesen in einer Schleife erfolgt.

Das war missverständlich. Ich meinte das in Bezug auf mehrere Lesezugriffe und TLightweightMREW, aber das muss man natürlich beim Lesen und Schreiben verwenden. Ich korrigiere die Formulierung. Danke.

AJ_Oldendorf 29. Jan 2025 07:19

AW: Synchronisierung zwischen Threads ohne Mutex
 
Danke für die Anregungen.
Werde mich mal mit TMonitor beschäftigen.
Habt ihr da mal ein einfaches Beispiel, wie man das damit machen könnte?

jaenicke 29. Jan 2025 23:27

AW: Synchronisierung zwischen Threads ohne Mutex
 
Du musst lediglich ein Objekt angeben, das für die Identifizierung der Sperre verwendet wird. Da es hier um Zugriffe auf eine Liste geht, bietet es sich an, diese auch zu verwenden.
Delphi-Quellcode:
  TMonitor.Enter(MyList);
  try
    MyList.Add(42);
  finally
    TMonitor.Exit(MyList);
  end;
Entsprechend mit Angabe des Starts und Endes des Blocks funktionieren auch die andere Varianten wie z.B. TLightweightMREW:
Delphi-Quellcode:
var
  Lock: TLightweightMREW;
  Test: Integer;
begin
  Lock.BeginWrite; // Schreibzugriff anfordern
  try
    MyList.Add(42);
  finally
    Lock.EndWrite;
  end;

  Lock.BeginRead; // Lesezugriff anfordern
  try
    Test := MyList[0];
  finally
    Lock.EndRead;
  end;
Wichtig ist try..finally, um sicherzustellen, dass auch bei einem Fehler die Sperre wieder aufgehoben wird.

AJ_Oldendorf 30. Jan 2025 09:02

AW: Synchronisierung zwischen Threads ohne Mutex
 
Delphi-Quellcode:
  TMonitor.Enter(MyList);
  try
    MyList.Add(42);
  finally
    TMonitor.Exit(MyList);
  end;
Das heißt, wenn ich in dem try-Abschnitt (zwischen dem .Enter und .Exit) mit Task 1 bin, bleibt eine Task 2 im ".Enter" hängen, solange die Task 1 das ".Exit" nicht durchgeführt hat im finally?

QuickAndDirty 30. Jan 2025 09:09

AW: Synchronisierung zwischen Threads ohne Mutex
 
TMonitor ist eine CriticalSection die Du nicht global deklariert hast sondern die seit Delphi Version *MPF* in jedem Nachfahren von TObjekt deklariert ist.
Dadurch kann du also einzelne Objekte für alle threads in denen sie benutzt werden als "in Benutzung" markieren und wieder freigeben.
Wenn du alle Verwendungen eines Objekts mit TMonitor einrahmst, dann ist das Objekt über verschiedene threads hinweg als gemeinsame Resource sicher nutzbar.

jaenicke 30. Jan 2025 09:25

AW: Synchronisierung zwischen Threads ohne Mutex
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1545773)
Das heißt, wenn ich in dem try-Abschnitt (zwischen dem .Enter und .Exit) mit Task 1 bin, bleibt eine Task 2 im ".Enter" hängen, solange die Task 1 das ".Exit" nicht durchgeführt hat im finally?

Ja, das ist richtig. Und bei der TLightweightMREW können beliebig viele parallel lesen, aber sobald ein Schreibzugriff angefordert wird, wird im BeginWrite gewartet, bis alle Zugriffe beendet sind (und es darf niemand mehr rein, auch nicht nur lesend) und dann wird exklusiv der Schreibblock betreten.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:58 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 by Thomas Breitkreuz