AGB  ·  Datenschutz  ·  Impressum  







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

Record threadsicher verwenden

Ein Thema von MechMac666 · begonnen am 15. Mär 2021 · letzter Beitrag vom 17. Mär 2021
Antwort Antwort
Seite 1 von 2  1 2      
MechMac666

Registriert seit: 9. Nov 2008
95 Beiträge
 
#1

Record threadsicher verwenden

  Alt 15. Mär 2021, 15:44
Hallo,

ich habe eine Konsolenanwendung mit einem Record in einem Array, welches von diversen Threads verwendet werden soll.
Sowohl lesen als auch schreiben.

Das Array wird von einem eigenen Thread abgearbeitet und von anderer Seite wieder aufgefüllt.
Es ist also eine Queue.

Der Queue Thread sieht wie folgt aus:


Delphi-Quellcode:



 type Tsend_entry=record
    Frame:TWebSocketFrame;
    recipients:THttpServerConnectionIDDynArray;
 end;


 var
   send_queue:array of Tsend_entry;



procedure DeleteArrayElement(const AIndex: Integer);
begin
  Move(send_queue[AIndex + 1], send_queue[AIndex], SizeOf(send_queue[0]) * (Length(send_queue) - AIndex - 1)); //Dahinterliegende Daten aufrücken
  SetLength(send_queue, Length(send_queue) - 1); // Länge kürzen
end;



procedure TQueueSendThread.Execute;
begin
  while not stop_thread do
  begin
    sleep(100);
    if high(send_queue)>=0 then
    if not array_lock then
    begin
      array_lock:=true; //beides muss zusammen ausgeführt werden
      sleep(10);
      server.WebSocketBroadcast( send_queue[0].frame,send_queue[0].recipients);
      DeleteArrayElement(0);
      array_lock:=false;
    end;
  end;
end;
Die Threads zum Auffüllen berücksichtigen den Arraylock ebenfalls.
Theoretisch ist das Array also gesperrt wenn es in schreibender Verwendung ist.

Dennoch habe ich mit Problemen zu kämpfen, wo ich sagen würde, das es mit dem Timing zusammen hängt.
Denn zwischen prüfung ob der Lock gesetzt ist und anschließenden setzen, könnte ein anderer Thread zuvorkommen.


Zuvor hatte ich folgendes versucht:
Delphi-Quellcode:
      server.WebSocketBroadcast( send_queue[0].frame,send_queue[0].recipients);
      TThread.Synchronize(nil, procedure
      begin
        DeleteArrayElement(0);
      end);
Aber das klappte gar nicht erst.


Wie kann ich den Zugriff threadsicher gestalten?
Oder liegt es vielleicht auch schon daran, das ich den Arraylock nur beim schreiben setze?
  Mit Zitat antworten Zitat
jziersch

Registriert seit: 9. Okt 2003
Ort: München
251 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 15:49
Mit einem Objekt der Klasse TMultiReadExclusiveWriteSynchronizer aus System.SysUtils kannst Du den Lese und Schreibzugriff steuern.

Aufpassen: BeginWrite gibt einen Boolean zurück, der besagt aber nur, dass evtl. gelesene Variablen ungültig geworden sein könnten.

Die Funktion Synchronize ist der Anfang von vielem Übel, am besten nicht verwenden.
WPCubed GmbH
Komponenten für Delphi:
WPTools, wPDF, WPViewPDF
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.176 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 15:50
Nicht das Rad neu erfinden.

http://docwiki.embarcadero.com/RADSt...riff_vermeiden
  Mit Zitat antworten Zitat
jziersch

Registriert seit: 9. Okt 2003
Ort: München
251 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 15:54
Statt Deinem ArrayLock kannst Du auch TMonitor.TryEnter(EinObjekt) verwenden.
WPCubed GmbH
Komponenten für Delphi:
WPTools, wPDF, WPViewPDF
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 15:55
Delphi-Referenz durchsuchenTThreadList<Tsend_entry> , da ist gleich alles für die Synchronisierung integriert.


Add, Remove, LockList/UnlockList


Schade, dass es hier kein Push/Pop wie in Delphi-Referenz durchsuchenTStack bzw. Delphi-Referenz durchsuchenTQueue gibt.


Und jupp, "dieses" TMonitor ist eine Art CriticalSection, die man "in" jedes TObjekt legen kann. (nicht zu Verwechseln mit TMonitor für den Bildschirm ... der Name wurde dämlicher Weise so von C# geklaut)



Hat jeder Thread seinen eigenen Server?
Wenn nicht, dann aufpassen, ob WebSocketBroadcast thread-save ist und wenn nicht, dann muß das auch synchronisiert werden.
$2B or not $2B

Geändert von himitsu (15. Mär 2021 um 15:59 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 16:05
PS:
Delphi-Quellcode:
if not array_lock then
begin
  array_lock:=true; //beides muss zusammen ausgeführt werden
Jupp, es muß "zusammen", denn rate mal was passiert, wenn zwei threads gleichzeitig das IF prüfen, noch bevor Einer das auf True gestellt hat ... dann sind zwei Drin.

Lösung:
* eine "richtige" Sperre drumrum (CriticalSection, MultiReaderWriter, ...)
* oder als "atomare" Operation das Vergleichen+Zuweisen, wie z.B. MSDN-Library durchsuchenInterlockedCompareExchange bzw. Delphi-Referenz durchsuchenAtomicCmpExchange (und LOCK im Assembler)
$2B or not $2B

Geändert von himitsu (15. Mär 2021 um 16:13 Uhr)
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
472 Beiträge
 
#7

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 16:10
Da du mORMot verwendest, schaue in die Unit SynTable. Dort findest du die Klasse TSynQueue.
Code:
/// thread-safe FIFO (First-In-First-Out) in-order queue of records
// - uses internally a dynamic array storage, with a sliding algorithm
Ein Beispiel für die Anwendung findest du in der Unit SynSelfTests.

Oder in der Unit SynCommons die Klasse TSynDictionary.

Bis bald...
Thomas
  Mit Zitat antworten Zitat
Alt 15. Mär 2021, 16:33     Erstellt von dummzeuch
Dieser Beitrag wurde von mkinzler gelöscht.
MechMac666

Registriert seit: 9. Nov 2008
95 Beiträge
 
#8

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 22:21
Also mit TSynQueue hat es super geklappt. Danke für den Tipp.

Aber ich suche noch etwas für meine Userliste.

Der Gedanke wäre eine TThreadlist welche folgendes verwaltet.
Delphi-Quellcode:
 type TUser=record
    ID:int64;
    UserName:string;
    LoginCount:integer;
 end;

Jedoch sind alle Infos die ich dazu gesehen habe mit Klassen oder Objekten gemacht.
Ich bekomme es gerade absolut nicht hin da ein Record einzufügen.
Mit einer Variablen welche auf den Record pointet klappt das einfügen.
Delphi-Quellcode:
var ptUser:^TUser;
...
List := Userlist.LockList;
List.Add(ptUser);
...
Wenn ich ptUser.UserName vorher etwas zuweise und dann über list[0] versuche das zurückzulesen, gibt es ne Schutzverletzung.
Naja, irgendwie klar, denn ptUser pointet ja nur auf den Typ.

Oder muss ich von TUser ein Array erstellen und dessen Elemente der Threadlist zuweisen?


EDIT
Habe noch etwas gefunden wonach ich zunächst "new(ptUser)" aufrufen muss.
Mir ist aber noch nicht ganz klar, wo sich das erzeugte Objekt dann befindet und wie ich es erreiche.
Vor allem wie ich dann z.B. mit List[0] wieder auf den Typ TUser komme.

Geändert von MechMac666 (15. Mär 2021 um 22:41 Uhr)
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
472 Beiträge
 
#9

AW: Record threadsicher verwenden

  Alt 15. Mär 2021, 22:59
Aber ich suche noch etwas für meine Userliste.
Ich habe jetzt keine Möglichkeit zum Test, das sollte aber so funktionieren.
Delphi-Quellcode:
type
  TUser = record
    UserName: String;
    LoginCount: Integer;
  end;

  TUserID = Int64;
  TUserIDDynArray = array of TUserID;
  TUserDynArray = array of TUser;

FUserList := TSynDictionary.Create(TypeInfo(TUserIDDynArray), TypeInfo(TUserDynArray), False, {TimeoutSeconds=} 3600);
  
var
  user: TUser;
begin
  user.UserName := '';
  user.LoginCount := 2;
  FUserList.AddOrUpdate(userID, user);
Mit der Angabe TimeoutSeconds kannst du durch Aufruf von DeleteDeprecated() ältere Einträge rausschmeißen (hier 1 Stunde).

Bis bald...
Thomas
  Mit Zitat antworten Zitat
MechMac666

Registriert seit: 9. Nov 2008
95 Beiträge
 
#10

AW: Record threadsicher verwenden

  Alt 16. Mär 2021, 22:17
Ich probiere das gerade aus. Soweit passt das TSynDictionary sehr gut in das Konzept.
Allerdings bin ich nun bei meiner Broadcastliste hängen geblieben, welche ich im Zuge der Umstrukturierung auch ändern sollte.

Der Plan war/ist ein Array welches die ID's der eingeloggten User beinhaltet und nur bei Login/Logoff aktivität geupdatet wird.
Es wird für jede ausgehende Nachricht verwendet.
Der Zugriff darauf muss also auch threadsicher sein.

var logged_users:THttpServerConnectionIDDynArray;
Falls ich das Array nicht threadsicher führen kann, besteht die Möglichkeit sie aus dem TSynDictionary abzuleiten.
Aber es will mir nicht gefallen, für jede Nachricht diesen aufwändigen Prozess zu wiederholen.
Ich kann mir aus dem TSynDictionary die Values herausschreiben lassen um an die potenziellen Benutzer zu kommen, jedoch fehlt mit der Key dazu.
Oder anders ausgedrückt:
Delphi-Quellcode:
 type
  TUser = record
    UserName: String;
    LoginCount: Integer;
  end;

  TUserID = Int64;
  TUserIDDynArray = array of TUserID; //Key
  TUserDynArray = array of TUser; //Value
Ich würde dann jeden Key benötigen, wo der UserName<>'' ist.


Theoretisch könnte ich jetzt auch wieder ein TSynDictionary dafür missbrauchen indem ich key=Value setze.
Oder aber ich füge dem bestehenden TSynDictionary bei TUser noch die ID hinzu.
Nur das ist dann ja auch irgendwie doppelt gemoppelt wenn der Key nochmals in der Struktur der Value auftaucht.

Oder eben eine TThreradlist für das THttpServerConnectionIDDynArray. (Sofern ich es gebacken bekomme das darin abzubilden)
  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 22:30 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