![]() |
Zugriff von Threads auf Instanzen, wo CriticalSection def.?
hallo,
ich beziehe mich hierbei auf ![]() und zwar geht es darum, dass ich eine klasse habe, die datenbankabfragen tätigt. diese hat drei public-methoden, die allerdings, nur mit angepassten parametern, eine private-methode aufrufen. hier vorab schon mal der 1:1 code von der private-methode.
Delphi-Quellcode:
die public-methoden, die eben die private-methode aufrufen, haben die folgenden aufgaben: einerseits das handling von select-abfragen, also mit ergebnis-tabelle, zum anderen inserts, delete etc, mit ergebnis über den erfolg und das dritte wäre die transaktion (nur inserts, delete etc.), eben auch mit dem ergebnis über erfolg der queries.
// private-Methode
function TDatabase._execSQL(SQL: String; var ATable: TADDatSTable): Boolean; var cmd : Char; b : Boolean; sqlList : TStringList; s : String; begin Result := False; SQL := trim(SQL); // Auf Verbindung prüfen if not FConnection.Connected then exit; try // Länge des SQL-Befehls prüfen if (length(SQL) < 1) then raise Exception.Create('Es wurde kein SQL-Befehl übergeben'); // SQL-Befehl prüfen cmd := SQL[1]; if (not (cmd in ALLOWED_DATABASE_COMMANDS) ) then raise Exception.Create('Es wurde kein korrekter SQL-Befehl übergeben'); // Unerlaubte Zeichen auskommentieren (für Oracle) maskSpecialChars(SQL); sqlList := TStringList.Create; try // SQL in Zeilen aufsplitten, in so fern #13 im SQL-String vorkommen while pos(#13, SQL) > 0 do begin s := copy(SQL, 1, pos(#13, SQL) - 1); delete(SQL, 1, pos(#13, SQL)); sqlList.Add(trim(s)); end; sqlList.Add(SQL); // letzter Part bzw alleiniger SQL-Befehl ohne mehrere Zeilen // Kritische Sektion betreten FCriticalSection.Enter(); // SQL ausführen try FCommand.CommandText := sqlList; if (cmd = 'S') then begin try if not Assigned(ATable) then exit; // SELECT-Abfrage mit Ergebnisausgabe FCommand.Open; // Abfrage wird an DB gesendet und Ergebnis wird in Empfang genommen FCommand.Define(ATable); // Tabelle bekommt hier die Spalten mitgeteilt FCommand.Fetch(ATable, True); // Übertrag der empfangenen Daten an die Tabelle FCommand.Close; Result := True; except raise; end; end else begin // -> Exception, wenn die Abfrage nicht geklappt hat // ansonsten tritt kein Fehler auf try FCommand.Execute(); Result := True; except raise; end; end; finally // Kritische Sektion wieder freigeben FCriticalSection.Release(); end; except on E : Exception do begin // Wenn es zu einer internen Exception kommt, dann wird nur in die // Log-Datei geloggt, da es sonst unter Umständen zu einer Endlos- // schleife werden kann b := FLog.LogToDatabase; FLog.LogToDatabase := False; Log(E.Message, ltError); FLog.LogToDatabase := b; end; end; finally FreeAndNil(sqlList); end; end;
Delphi-Quellcode:
zudem ist noch zu sagen, dass ich die variable FCriticalSection : TCriticalSection in der klasse TDatabase private definiert habe.
function execSQL(aSQL: String): Boolean;
var tmp : TADDatSTable; begin { ... } Result := _execSQL(aSQL, tmp); { ... } end; function execSQL(aSQL: String; var aTable: TADDatSTable): Boolean; begin { ... } Result := _execSQL(aSQL, aTable); { ... } end; function execTransaction(aSQLList: TStringList): Boolean; var i : Integer; tmp begin { ... } { Transaktion wird gestartet} Result := True; for i := 0 to aSQLList.Count - 1 do begin if not (execSQL(aSQLList[i])) then begin Result := False; break; end; end; { Transaktion wird beendet } { ... } end; nun zum eigentlichen problem: in zeile 41 der private-methode betrete ich ja die kritische sektion und jeder andere thread, der eben ein query ausgeführt haben möchte wartet an dieser stelle -- so habe ich das bisher halt verstanden. wenn der thread, der gerade den code ausführt, d.h. andere threads blockiert, zeile 73 erreicht so gibt er diesen abschnitt für andere threads wieder frei und der nächste thread kann diesen code ausführen. blockiert allerdings wieder die anderen threads. die frage hierbei ist: ist das die richtige position, in so fern ich einen normalen query ausführe, also noch keine transaktion, da dies ein gesondertes problem darsellt??? was passiert mit den variablen innerhalb der methode _execSQL, wenn zwei threads mehr oder weniger gleichzeitig in diese methode rein laufen und diesen code ausführen, eben bis zur besagten stelle? was passiert dabei im speichern? werden zwei "instanzen" (ich weiß nicht, wie man dazu sagt) von dieser methode im speicher gehalten, oder nur eine? denn dann könnte ja das problem auftreten, dass die variablen in der methode überschrieben werden, eben ein zweiter thread die daten überschreibt, die der ausführende thread gerader braucht!? ich hoffe, ihr könnt mir hierbei etwas weiterhelfen. vielleicht noch eine frage am rande: wie lange wartet ein thread eigentlich, wenn er nicht sofort den code ausführen kann? halt er einfach an dieser stelle an, oder wird ein neues execute aufgerufen? hätte jemand evtl. ein sehr ausführliches tutorial über threads? wäre echt klasse. habe das von luckie schon gelesen, aber das geht mir nicht ausreichend ins detail! mit freundlichen grüßen der hai |
Re: Zugriff von Threads auf Instanzen, wo CriticalSection de
*push* :oops:
|
Re: Zugriff von Threads auf Instanzen, wo CriticalSection de
Auch wenn mehrere Threads eine Methode ausführen, gibt es den Code nur einmal im Speicher. Allerdings hat jeder Thread einen eigenen Stack. Da die lokalen Variablen der Methode auf dem Stack oder in Registern stehen, gibt es also keine Probleme, wenn mehrere Threads gleichzeitig mit ihren lokalen Variablen spielen.
Kritisch wird es allerdings, wenn globale Variablen oder Klassenfelder angesprochen werden. Dazu benötigt man dann die CriticalSection. Es ist aber ungemein wichtig, daß sicher alle Zugriffe auf diese Variablen über die CriticalSection laufen, wenn die Möglichkeit besteht, daß auch andere Threads darauf zugreifen können. Ein mögliches Problem hast du z.B. in deiner Exception-Behandlung
Delphi-Quellcode:
Da wir hier wieder außerhalb der CriticalSection sind, ist das so alles andere als thread-sicher!b := FLog.LogToDatabase; FLog.LogToDatabase := False; Log(E.Message, ltError); FLog.LogToDatabase := b; Beispiel: - Thread A kommt in diese Code-Sequenz, FLog.LogToDataBase sei True, er merkt sich den Wert in b und setzt es auf False. - Thread B kommt zu der gleichen Sequenz, FLog.LogToDataBase ist jetzt false (von Thread A), er merkt sich das und - wird wiederum von Thread A unterbrochen, der sein Log ausführt und FLog.LogToDataBase wieder auf true setzt, wie es vorher war. - Nun darf Thread B weitermachen, der fälschlicherweise nun doch in die Database loggt und danach (noch falscher) FLog.LogToDataBase auf false setzt - mit dem Ergebnis, daß ab hier so ziemlich alles den Bach hinunter geht. Ohne das jetzt weiter analysiert zu haben, sollte die Exception-Behandlung besser mit in die CriticalSection genommen werden. Thread-Programmierung ist nun mal nicht ganz so einfach... |
Re: Zugriff von Threads auf Instanzen, wo CriticalSection de
[völlig OffTopic]
Hallo Uwe, wie kommst Du zu einem Zitat:
[/völlig Offtopic] Grüße, Messie |
Re: Zugriff von Threads auf Instanzen, wo CriticalSection de
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:59 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-2025 by Thomas Breitkreuz