AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Zugriff von Threads auf Instanzen, wo CriticalSection def.?
Thema durchsuchen
Ansicht
Themen-Optionen

Zugriff von Threads auf Instanzen, wo CriticalSection def.?

Ein Thema von s.h.a.r.k · begonnen am 19. Sep 2008 · letzter Beitrag vom 22. Sep 2008
Antwort Antwort
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#1

Zugriff von Threads auf Instanzen, wo CriticalSection def.?

  Alt 19. Sep 2008, 11:39
hallo,

ich beziehe mich hierbei auf diesen Thread, wobei diese fragestellung, in meinen augen, nichts mehr mit dem problem hier zu tun hat, daher ein neues Topic.

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:
// 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;
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.
Delphi-Quellcode:
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;
zudem ist noch zu sagen, dass ich die variable FCriticalSection : TCriticalSection in der klasse TDatabase private definiert habe.

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
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#2

Re: Zugriff von Threads auf Instanzen, wo CriticalSection de

  Alt 22. Sep 2008, 18:57
*push*
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.015 Beiträge
 
Delphi 12 Athens
 
#3

Re: Zugriff von Threads auf Instanzen, wo CriticalSection de

  Alt 22. Sep 2008, 19:28
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:
        
        b := FLog.LogToDatabase;
        FLog.LogToDatabase := False;
        Log(E.Message, ltError);
        FLog.LogToDatabase := b;
Da wir hier wieder außerhalb der CriticalSection sind, ist das so alles andere als thread-sicher!

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...
Uwe Raabe
  Mit Zitat antworten Zitat
messie

Registriert seit: 2. Mär 2005
Ort: Göttingen
1.592 Beiträge
 
Delphi 2009 Professional
 
#4

Re: Zugriff von Threads auf Instanzen, wo CriticalSection de

  Alt 22. Sep 2008, 21:29
[völlig OffTopic]
Hallo Uwe,
wie kommst Du zu einem
Zitat:
RAD-Studio 2009 Architect
????
[/völlig Offtopic]

Grüße, Messie
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.015 Beiträge
 
Delphi 12 Athens
 
#5

Re: Zugriff von Threads auf Instanzen, wo CriticalSection de

  Alt 22. Sep 2008, 22:21
Zitat von messie:
[völlig OffTopic]
Hallo Uwe,
wie kommst Du zu einem
Zitat:
RAD-Studio 2009 Architect
????
[/völlig Offtopic]

Grüße, Messie
Na ja, ein Delphi 2009 Architect stand nicht zur Auswahl und da mein SA auch dafür gut ist, habe ich eben das Produkt gewählt, das dem am nächsten kommt - zumal wir bis Jahresende sowas wohl auch noch zu sehen bekommen werden...
Uwe Raabe
  Mit Zitat antworten Zitat
Antwort Antwort


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 03:42 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