Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi CriticalSections wie verwenden? (https://www.delphipraxis.net/156318-criticalsections-wie-verwenden.html)

Bummi 28. Nov 2010 01:03

CriticalSections wie verwenden?
 
Umzug der CriticalSection Diskusion aus
verhindern: Klick auf Titelleiste stoppt Programm #27

http://www.delphipraxis.net/156304-v...ml#post1064523

@Sir Rufo
Zitat:

Aus diesem Grund hänge ich die Instanz von TCriticalSection auch direkt an den Thread, um dessen Properties so zu schützen, dass kein gleichzeitiger Zugriff darauf erfolgen kann. Mittels Getter und Setter wird dann automatisch die CS betreten/verlassen. Somit wird der Zugriff auf selbige extrem stressfrei.

Mir ist nicht ganz klar was Du damit schützt, die Bereiche innerhalb Deiner Klasse die nicht von anderen Threads erreicht werden können brauchen IMHO nicht mit einer CriticalSection gekapselt werden.

Wenn Du einen Bereich in eine CriticalSection packst mußt Du Dich so zumindest mein Kenntnissstand darauf verlassen können daß andere über die gleich CriticalSection darauf zugreifen.

Hast Du Punkt #26 aus o.g. Thread einmal nachvollzugen? Die Variable txt ist nur sicher zugreifbar wenn sich alle daran halten vor dem Zugriff ein und dieselbe CriticalSection zu verwenden.

Assarbad 28. Nov 2010 01:22

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064525)
Wenn Du einen Bereich in eine CriticalSection packst mußt Du Dich so zumindest mein Kenntnissstand darauf verlassen können daß andere über die gleich CriticalSection darauf zugreifen.

Absolut korrekt. Nur wenn sich alle daran halten, hilft der kritische Abschnitt.

Zitat:

Zitat von Bummi (Beitrag 1064525)
Die Variable txt ist nur sicher zugreifbar wenn sich alle daran halten vor dem Zugriff ein und dieselbe CriticalSection zu verwenden.

Jupp.

Ich nehme allerdings an, daß hier nur die Ausdrucksweise unglücklich war. Wenn der Getter und Setter (oder ggf. nur Setter, je nach Datentyp) den kritischen Abschnitt benutzen, ist das ja nicht auf einen Thread beschränkt, wie die Aussage erstmal vermuten läßt.

Bummi 28. Nov 2010 01:30

AW: CriticalSections wie verwenden?
 
Zitat:

Ich nehme allerdings an, daß hier nur die Ausdrucksweise unglücklich war. Wenn der Getter und Setter (oder ggf. nur Setter, je nach Datentyp) den kritischen Abschnitt benutzen, ist das ja nicht auf einen Thread beschränkt, wie die Aussage erstmal vermuten läßt.
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.

Assarbad 28. Nov 2010 01:48

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064527)
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.

Jupp, tut mir leid. Hatte das Originalthema nicht komplett gelesen und auch jetzt nur überflogen. Aber der Code bei dem jeder Thread seine eigene CS hat, ist natürlich Unsinn. Ein Synchronisationsobjekt funktioniert nur wenn alle das gleiche benutzen. Oder um Luckie zu zitieren:

Zitat:

Zitat von Luckie (Beitrag 1064510)
Wenn mehrere Threads aufeinander warten müssen, dann dürfen sie auch nur die gleiche CriticalSection nutzen. Das ist wie bei einer Ampelkreuzung, die darf auch nur von einer Schaltung gesteuert werden. Hätte jede Ampel ihre eigene Steuerung würde es krachen.


Sir Rufo 28. Nov 2010 01:57

AW: CriticalSections wie verwenden?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Um das mit den CriticalSections mal etwas besser zu verdeutlichen habe ich mal mein WorkThread-Beispiel etwas umgeändert um folgende Situationen darzustellen:
- optimales Laufzeitverhalten mit einem Minimum an Störungen des Threads durch Zugriffe von außen (bzw. durch den Event, der ja in einem anderen Thread-Kontext läuft und somit auch einen Zugriff von außen darstellt)
- blockierndes Laufzeitverhalten durch ein ungeschicktes Verwenden von CS
- Deadlock durch CS

Mit den beiden Compilerschaltern kann der Thread entsprechend eingestellt werden.
Delphi-Quellcode:
unit uWorkThread;

{.$DEFINE DEADLOCK}
{.$DEFINE BLOCKING}

interface

uses
  Generics.Collections,
  Classes,
  SyncObjs;

type
  TWorkItem = record
    TimeStamp : TDateTime;
  end;

  TWorkQueue = TQueue< TWorkItem >;

  TWorkNotify = procedure( WorkItem : TWorkItem ) of object;
  TWorkType = ( wtSync, wtQueue );

  TWorkThread = class( TThread )
  private
    { Private-Deklarationen }

    FCSWork : TCriticalSection;
    FCSQueue : TCriticalSection;
    FCSWorkType : TCriticalSection;

    FWorkQueue : TWorkQueue;
    FOnWork : TWorkNotify;
    FWorkType : TWorkType;

    procedure DoOnWork;
    procedure SetOnWork( const Value : TWorkNotify );
    function GetOnWork : TWorkNotify;
    procedure SetWorkType( const Value : TWorkType );
    function GetWorkType : TWorkType;

  protected
    procedure Execute; override;

  public
    property WorkType : TWorkType read GetWorkType write SetWorkType;
    property OnWork : TWorkNotify read GetOnWork write SetOnWork;
    constructor Create( CreateSuspended : Boolean );
    destructor Destroy; override;
  end;

implementation

uses
  SysUtils;

{ TProgressThread }

constructor TWorkThread.Create( CreateSuspended : Boolean );
  begin
    FCSWork := TCriticalSection.Create;
    FCSQueue := TCriticalSection.Create;
    FCSWorkType := TCriticalSection.Create;

    FCSWork.Enter;
    FCSQueue.Enter;
    FCSWorkType.Enter;
    try
      inherited;

      FWorkQueue := TWorkQueue.Create;
      FWorkType := wtSync;

    finally
      FCSWork.Leave;
      FCSQueue.Leave;
      FCSWorkType.Leave;
    end;
  end;

destructor TWorkThread.Destroy;
  begin
    FCSWork.Enter;
    FCSQueue.Enter;
    FCSWorkType.Enter;
    try

      FWorkQueue.Free;

      inherited;
    finally
      FCSWork.Leave;
      FCSQueue.Leave;
      FCSWorkType.Leave;
      FreeAndNil( FCSWork );
      FreeAndNil( FCSQueue );
      FreeAndNil( FCSWorkType );
    end;
  end;

procedure TWorkThread.DoOnWork;
  var
    WorkItem : TWorkItem;
  begin

    FCSWork.Enter;
    try
      WorkItem := FWorkQueue.Dequeue;
    finally
      FCSWork.Leave;
    end;

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      if Assigned( FOnWork ) then
        FOnWork( WorkItem );

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;

  end;

procedure TWorkThread.Execute;
  var
    WorkItem : TWorkItem;
    Counter : Integer;
  begin
    { Thread-Code hier einfügen }
    Counter := 0;
    while not Terminated and ( Counter < 1000 ) do
      begin

        WorkItem.TimeStamp := Now;
        Inc( Counter );

        FCSWork.Enter;
        try
          FWorkQueue.Enqueue( WorkItem );
        finally
          FCSWork.Leave;
        end;

        {$IFDEF DEADLOCK}

        FCSWork.Enter;
        try

          {$ENDIF}

          case WorkType of
            wtSync :
              Synchronize( DoOnWork );
            wtQueue :
              Queue( DoOnWork );
          end;

          {$IFDEF DEADLOCK}

        finally
          FCSWork.Leave;
        end;

        {$ENDIF}

        //Sleep( 10 );
      end;
  end;

function TWorkThread.GetOnWork : TWorkNotify;
  begin

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      Result := FOnWork;

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;
  end;

function TWorkThread.GetWorkType : TWorkType;
  begin
    FCSWorkType.Enter;
    try
      Result := FWorkType;
    finally
      FCSWorkType.Leave;
    end;
  end;

procedure TWorkThread.SetOnWork( const Value : TWorkNotify );
  begin

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      FOnWork := Value;

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;
  end;

procedure TWorkThread.SetWorkType( const Value : TWorkType );
  begin
    FCSWorkType.Enter;
    try
      FWorkType := Value;
    finally
      FCSWorkType.Leave;
    end;
  end;

end.
Wenn man jetzt die Laufzeiten von optimal und blockierend betrachtet (deadlock läuft ja gar nicht) dann sieht man folgendes:
Erstellung: Zeitpunkt der Erstellung im Thread
Ausgabe: Zeitpunkt der Ausgabe in der ListBox
optimal
Code:
Erstellung    Ausgabe
============  ============
01:43:52.380 - 01:43:52.381
01:43:52.380 - 01:43:52.382
01:43:52.380 - 01:43:52.384
01:43:52.380 - 01:43:52.392
01:43:52.380 - 01:43:52.394
01:43:52.380 - 01:43:52.395
01:43:52.380 - 01:43:52.397
01:43:52.380 - 01:43:52.397
01:43:52.380 - 01:43:52.399
...
01:43:52.386 - 01:43:53.961
Der Thread hat nach 6/1000 Sekunden alle 1000 Einträge erzeugt.
Bis zu diesem Zeitpunkt wurden 3 Einträge in die ListBox eingetragen.
Erzeugung und Ausgabe laufen aber parallel, die Ausgabe dauert halt nur länger als die Erstellung

blockierend

Code:
Erstellung    Ausgabe
============  ============
01:42:10.625 - 01:42:10.626 <- Ausgabe erfolgt
01:42:10.625 - 01:42:10.626
...
01:42:10.626 - 01:42:10.671
01:42:10.626 - 01:42:10.673
01:42:10.626 - 01:42:10.675 <- Erstellung stoppt
01:42:10.673 - 01:42:10.677 <- Erstellung geht weiter
01:42:10.673 - 01:42:10.679
01:42:10.673 - 01:42:10.681
...
hier erfolgt nun entweder die Ausgabe oder die Erstellung, aber keine parallele Verarbeitung.

Source und Exe im Anhang

jaenicke 28. Nov 2010 02:05

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064527)
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.

Wenn es um den Zugriff auf Resourcen des Threads geht (wie in dem Beispiel, um das es geht), dann reicht auch eine threadlokale kritische Sektion. Denn da im Getter und Setter diese genauso benutzt wird wie innerhalb des Threads, ist kein paralleler Zugriff auf das private Feld hinter Getter bzw. Setter möglich.

Der Zugriff aus einem anderen Thread passiert ja über die Getter und Setter bzw. die entsprechende Property und damit wird der Zugriff auch effektiv geschützt. Und dabei muss dennoch nicht mit dem Hauptthread synchronisiert werden, sondern es wird nur die kritische Sektion benutzt.

Nicht funktionieren tut es, wenn der Zugriff auf eine Resource eben nicht immer über die selbe kritische Sektion erfolgt. Aber das ist hier nicht der Fall, da jeder Thread zwar eine eigene kritische Sektion, aber auch eine eigene zu schützende Resource hat.

Bummi 28. Nov 2010 02:10

AW: CriticalSections wie verwenden?
 
Danke , aber wenn jeder Thread seien eigen CriticalSection erzeugt wie ist dann ein übergreifender Zugriff gewährleistet, oder da dies ja nicht eigentlich das Problem hier ist, wofür benötige ich die Threadinternen CS überhaupt?

Sir Rufo 28. Nov 2010 02:12

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Assarbad (Beitrag 1064529)
Zitat:

Zitat von Bummi (Beitrag 1064527)
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.

Jupp, tut mir leid. Hatte das Originalthema nicht komplett gelesen und auch jetzt nur überflogen. Aber der Code bei dem jeder Thread seine eigene CS hat, ist natürlich Unsinn. Ein Synchronisationsobjekt funktioniert nur wenn alle das gleiche benutzen.

Hmmm, das sehe ich anders, wenn ich optimal laufenden Code erzeugen will mit der maximal möglichen Performance. Natürlich wird es auch mit einer externen CS funktionieren, aber eben nicht so performant wie es laufen könnte.

Beispiel:
Delphi-Quellcode:
GlobalCS : TCriticalSection;

TMyThread = class( TThread )
{...}
  Info : Int64;
end;

procedure TMyThread.Execute;
  begin
    while not Terminated do
      begin
        GlobalCS.Enter;
        try
          QueryPerformanceCounter( Info );
        finally
          GlobalCS.Leave;
        end;
      end;
  end;

MyThread1, MyThread2 : TMyThread;

procedure ImMainThread;
var
  ThInfo : Int64;
begin
  GlobalCS.Enter;
  // Beide Threads MyThread1, MyThread2 stoppen jetzt, weil sie ja
  // GlobalCS nicht mehr betreten können.
  // Ich will aber nur von MyThread1 etwas wissen, warum soll MyThread2 dann gestört werden???
  try
    ThInfo := MyThread1.Info;
  finally
    GlobalCS.Leave;
  end;
end;
Meine Variante sieht so aus
Delphi-Quellcode:

TMyThread = class( TThread )
  ThreadCS : TCriticalSection;
  FInfo : int64;
{...}
  property Info : Int64 read GetInfo;
end;

function GetInfo : Int64;
  begin
    ThreadCS.Enter;
    try
      Result := FInfo;
    finally
      ThreadCS.Leave;
    end;
  end;

procedure TMyThread.Execute;
  begin
    while not Terminated do
      begin
        ThreadCS.Enter;
        try
          QueryPerformanceCounter( FInfo );
        finally
          ThreadCS.Leave;
        end;
      end;
  end;

MyThread1, MyThread2 : TMyThread;

procedure ImMainThread;
var
  ThInfo : Int64;
begin
  ThInfo := MyThread1.Info;
end;
Der Zugriff auf die Property ist wesentlich leichter und es wird nur der Thread gestört, den ich abfrage.

Sir Rufo 28. Nov 2010 02:16

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064534)
Danke , aber wenn jeder Thread seien eigen CriticalSection erzeugt wie ist dann ein übergreifender Zugriff gewährleistet, oder da dies ja nicht eigentlich das Problem hier ist, wofür benötige ich die Threadinternen CS überhaupt?

a. Performance
b. der Thread selber ist per Definition auch threadsafe für Zugriffe von außen (über die Properties)

meine Threads übernehme ich in jedes beliebige Projekt und benutze diese ohne mich um irgendeine CS weiter zu kümmern, dann das regelt der Thread ja in sich selber.

Greift Thread A auf eine Property aus Thread B zu, dann ist die Property von Thread B ja geschützt. Somit kann es einfach keine Zugriffsfehler geben, da diese immer geschützt sind.

Ist quasi die Merz-Spezial-Dragee-Methode (Wahre Schönheit kommt von innen) :mrgreen:

Bummi 28. Nov 2010 02:21

AW: CriticalSections wie verwenden?
 
@Sir Rufo
Das eben macht mir Kopfschmerzen #9 b
Wenn Du für jede Instanz eine eigene CS erzeugst wie soll der Zugriff dann geschützt sein.

Aber ich muß jetzt ins Bett, gerne morgen weiter:thumb:

jaenicke 28. Nov 2010 02:23

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064534)
Danke , aber wenn jeder Thread seien eigen CriticalSection erzeugt wie ist dann ein übergreifender Zugriff gewährleistet, oder da dies ja nicht eigentlich das Problem hier ist, wofür benötige ich die Threadinternen CS überhaupt?

Der übergreifende Zugriff auf die Resource des Threadobjektes passiert ja über die dazu gehörende kritische Sektion.

Das heißt es geht nicht darum, auf die selbe Resource mit verschiedenen kritischen Sektionen zuzugreifen, wie du offenbar vermutest.

Assarbad 28. Nov 2010 02:24

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1064535)
Hmmm, das sehe ich anders, wenn ich optimal laufenden Code erzeugen will mit der maximal möglichen Performance. Natürlich wird es auch mit einer externen CS funktionieren, aber eben nicht so performant wie es laufen könnte.

Nunja, Synchronisation muß aber nunmal sein.

Meine Delphikenntnisse sind ein wenig eingerostet, aber ich sehe in deinem Code keinen konkurrierenden Zugriff zwischen den Threads. Was willst du also beweisen? Daß dein Code sicher ist, wäre zumindest damit nicht (generell) bewiesen.

Zitat:

Zitat von Sir Rufo (Beitrag 1064536)
Greift Thread A auf eine Property aus Thread B zu, dann ist die Property von Thread B ja geschützt. Somit kann es einfach keine Zugriffsfehler geben, da diese immer geschützt sind.

Die Aussage macht allerdings in der Tat Sinn, wenn ein Zugriff so wie beschrieben stattfände.

Das was ich gesehen habe wäre aber ohnehin billiger mit Interlocked-Funktionen zu machen (zumindest bei den verwendeten Datentypen).

Zitat:

Zitat von Bummi (Beitrag 1064537)
Wenn Du für jede Instanz eine eigene CS erzeugst wie soll der Zugriff dann geschützt sein.

Wenn jeder Thread seine eigene Queue hat und mit der CS schützt, dann macht es schon so Sinn. Da der Zugriff über Getter/Setter stattfindet, hat alles seine Ordnung.

Zusammenfassend:

Also, es gibt nicht nur eine Queue, somit ist die Queue auch pro Thread schützbar (so wie gezeigt).

Sir Rufo 28. Nov 2010 02:31

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064537)
@Sir Rufo
Das eben macht mir Kopfschmerzen #9 b
Wenn Du für jede Instanz eine eigene CS erzeugst wie soll der Zugriff dann geschützt sein.

Aber ich muß jetzt ins Bett, gerne morgen weiter:thumb:

Weil jede Thread-Instanz einen eigenen Speicherbereich hat, wo die Werte abgelegt sind.

Und ich schütze mit der Thread-internen CS den Speicherbereich der Instanz und nicht von allen Instanzen, was zwar möglich ist, aber überflüssig.

Stell dir vor ein Kühlschrank darf nur immer von einem geöffnet werden, weil sonst geht die Welt unter.

Bei deiner Methode mit der globalen CS würde das bedeuten, dass auf der ganzen Welt niemand einen Kühlschrank öffnen darf, nur weil ich gerade meinen geöffnet habe (da hatte ich aber Glück). Und erst wenn ich die Türe schließe, darf der nächste. Wenn ich böse bin, dann pfeife ich auf die Stromkosten, bzw. melde mich bei "e wie einfach" an und lasse den Rest der Welt verdursten.

Bei meiner Methode gilt das nur für den Kühlschrank, von dem ich etwas nehmen will. Alle anderen Kühlschränke interessieren mich nicht (und laut Definition auch nicht notwendig, da jeder Kühlschrank ja auch einen eigenen Speicherbereich hat, die ich nicht alle schützen muss, wenn ich meinen Kühlschrank öffne).

Bummi 28. Nov 2010 02:37

AW: CriticalSections wie verwenden?
 
Um bei Deinem Beispiel zu bleiben
Point.x
Point.y
in einer CriticalSection, wenn der lesende sich nicht darum schert daß es da eine (ihm unbekannte CS) gibt liest er einfach den Wert aus , egal ob der Thread es in eine CS gepackt hat oder nicht.

Bummi 28. Nov 2010 02:40

AW: CriticalSections wie verwenden?
 
@assabard
Delphi-Quellcode:
Wenn jeder Thread seine eigene Queue hat und mit der CS schützt, dann macht es schon so Sinn. Da der Zugriff über Getter/Setter stattfindet, hat alles seine Ordnung.
aber wofür benötigt er dann ene CS?

Sir Rufo 28. Nov 2010 02:41

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064542)
@assabard
Delphi-Quellcode:
Wenn jeder Thread seine eigene Queue hat und mit der CS schützt, dann macht es schon so Sinn. Da der Zugriff über Getter/Setter stattfindet, hat alles seine Ordnung.
aber wofür benötigt er dann ene CS?

Weil die Queue aus einem anderen Thread-Kontext abgefragt wird (Synchronize/Queue)

Sir Rufo 28. Nov 2010 02:42

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064541)
Um bei Deinem Beispiel zu bleiben
Point.x
Point.y
in einer CriticalSection, wenn der lesende sich nicht darum schert daß es da eine (ihm unbekannte CS) gibt liest er einfach den Wert aus , egal ob der Thread es in eine CS gepackt hat oder nicht.

Es muss aber zwingend in einer CS geschrieben/gelesen werden, ansonsten kann es dazu kommen, dass gleichzeitig geschrieben/gelesen wird und dann knallt es

Es ist egal, ob das von einer lokalen (im Thread verankerten) oder globalen CS geschützt wird. Es muss aber geschützt werden.

Aber wozu alles schützen, wenn ich genau bestimmen kann, was geschützt werden soll? (Sippenhaft und Singleton sind entweder nicht zulässig oder verpönt) :mrgreen:

Bummi 28. Nov 2010 02:50

AW: CriticalSections wie verwenden?
 
Jepp, aber hier kommen wir wieder zu dem Punkt daß die Abfrage sich nicht für Deine lokalen CS interessiert.
wenn Du nochmals mein Beispiel aus
http://www.delphipraxis.net/156304-v...ml#post1064523
#26 heranziehst
Meine Vorstellung einer CS basiert darauf daß ein Codeabschnitt markiert Windows mitgeteilt wird. Wenn ein anderer Thread versucht diesen Codeabschnitt zu betreten wird dies nur ermöglicht wenn kein anderer Thread sich gerade darin aufhält. Wenn die Markierung jedes mal eine andere ist wie soll Windows hier eingreifen.

Bummi 28. Nov 2010 02:55

AW: CriticalSections wie verwenden?
 
Zitat:

Es ist egal, ob das von einer lokalen (im Thread verankerten) oder globalen CS geschützt wird. Es muss aber geschützt werden.
ich bin völlig bei Dir, aber ohne Kenntnis / Beachtung des CS wir jeder zu jedem Zeitpunkt gegf. Müll auslesen(der Speicher ist ja nicht geschützt, Windows verhindert ja nur die Reentranz in ihm bekannte CS) .

jaenicke 28. Nov 2010 02:56

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064545)
wenn Du nochmals mein Beispiel aus
http://www.delphipraxis.net/156304-v...ml#post1064523
#26 heranziehst

Das ist aber ein vollkommen anderer Fall als der aus seinem Beispiel. Hier hast du eine globale Variable, auf die eben von verschiedenen Stellen aus zugegriffen wird. Das ist ja etwas anderes.

In seinem Beispiel befindet sich das Feld, auf das zugegriffen wird, innerhalb des Threads. Und es wird ausschließlich über die kritische Sektion genau dieses Threads zugegriffen. Und eben nicht durch verschiedene Threads und mehrere Sektionen. Denn beim Zugreifen verwendet auch der andere Thread die kritische Sektion des Zielthreads und nicht seine eigene.

Bummi 28. Nov 2010 03:01

AW: CriticalSections wie verwenden?
 
@jaenicke
Nochmals Danke
dies ist mir meine ich klar, aber wofür dann die CS wenn sich ohnehin alles innerhalb des Threads abspielt.
Ich scheine hier tatsächlich einen resistenten Hänger zu haben, mir erschließt sich der Sinn der lokalen CS nicht, die ja nur berücksichtigt werden können wenn ein potentieller "Zugreifer" davon Kenntnis hat.

jaenicke 28. Nov 2010 03:05

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064548)
aber wofür dann die CS wenn sich ohnehin alles innerhalb des Threads abspielt.

Tut es ja nicht. Ein anderer Thread (z.B. der Hauptthread) greift über die Eigenschaften des Zielthreads aus seinem Kontext zu. Innerhalb des Getters bzw. Setters wird dann über die kritische Sektion des Zielthreads, zu dem die Eigenschaft gehört, verhindert, dass gleichzeitig von innerhalb des Zielthreads darauf zugegriffen wird.

Sir Rufo 28. Nov 2010 03:07

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064545)
Jepp, aber hier kommen wir wieder zu dem Punkt daß die Abfrage sich nicht für Deine lokalen CS interessiert.
wenn Du nochmals mein Beispiel aus
http://www.delphipraxis.net/156304-v...ml#post1064523
#26 heranziehst
Meine Vorstellung einer CS basiert darauf daß ein Codeabschnitt markiert Windows mitgeteilt wird. Wenn ein anderer Thread versucht diesen Codeabschnitt zu betreten wird dies nur ermöglicht wenn kein anderer Thread sich gerade darin aufhält. Wenn die Markierung jedes mal eine andere ist wie soll Windows hier eingreifen.

Wenn ich Speicherbereich A schützen möchte, dann nehme ich dafür ein CS mit Namen CSA.
Ich muss nun gewährleisten, dass bei jedem Zugriff auf den Speicherbereich A die CS CSA betreten und danach wieder verlassen wird.

Das ist erstmal alles.

Windows ist es egal, wieviele CS ich benutze.

Jede Instanz von einer Klasse/Thread belegt einen bestimmten Speicherbereich. Erzeuge ich mit der Klasse/Thread eine CS kann ich damit genau den Speicherbereich vom der Klassen-/Thread-Instanz schützen.

Meine Vorgehensweise ist nun, dass alles in der Klasse/Thread, auf das von außen zugegriffen wird von eben so einer CS geschützt wird.

Delphi-Quellcode:
TMyThread = class( TThread )
  property Info : Int64 read GetInfo;
end;

function TMyThread.GetInfo : Int64;
  begin
    ThreadCS.Enter;
    try
      Result := FInfo;
    finally
      ThreadCS.Leave;
    end;
  end;
Wenn ich jetzt auf die Property Info von der Thread-Instanz zugreife, warum sollte die dann nicht geschützt sein?
Es wird ein CS betreten, der Wert ausgelesen und die CS wieder verlassen.
Das diese CS-Instanz innerhalb der Thread-Instanz aufgehangen ist, ist völlig egal. Es ist eine CS.

Und schützen muss ich die mit einer CS, wenn ich den Wert der Property Info innerhalb von TMyThread.Execute beschreiben will
Delphi-Quellcode:
procedure TMyThread.Execute;
  begin
    ThreadCS.Enter;
    try
      FInfo := 20;
    finally
      ThreadCS.Leave;
    end;
  end;

Bummi 28. Nov 2010 03:08

AW: CriticalSections wie verwenden?
 
warum funktioniert dann folgendes
http://www.delphipraxis.net/156304-v...ml#post1064523
#26

wenn hier der zugreifende die CS ignoriert, bwz eine ander nimmt bekommt er Zugriff die der Intention der CS wiederspricht.

Assarbad 28. Nov 2010 03:10

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064548)
dies ist mir meine ich klar, aber wofür dann die CS wenn sich ohnehin alles innerhalb des Threads abspielt.

Tut es nicht zwangsläufig.

Wir haben Threads A und B sowie Ressourcen a(A) und b(B) die jeweils dem Thread gehören. A besitzt eine CS die den Zugriff auf a(A) regelt und B eine CS die den Zugriff auf b(B) regelt. Soweit kommst du sicher mit, richtig?

Wenn nun aber Thread A einen Zugriff auf die Instanz von B macht, ruft er (im eigenen Kontext, also dem Kontext von Thread A ... das ist der potentielle Konflikt) die Getter oder Setter von B auf (aber mit Self == B). Dieser Getter oder Setter benutzt aber auch die CS welche den Zugriff auf b(B) regelt und damit ist alles in Ordnung.

jaenicke 28. Nov 2010 03:12

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064551)
wenn hier der zugreifende die CS ignoriert, bwz eine ander nimmt bekommt er Zugriff die der Intention der CS wiederspricht.

Dort hast du eine Resource und zwei kritische Sektionen, die du für den Zugriff benutzt.

In Sir Rufos Code hast du genau eine kritische Sektion für genau eine Resource.

Sir Rufo 28. Nov 2010 03:15

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064551)
warum funktioniert dann folgendes
http://www.delphipraxis.net/156304-v...ml#post1064523
#26

wenn hier der zugreifende die CS ignoriert, bwz eine ander nimmt bekommt er Zugriff die der Intention der CS wiederspricht.

Das läuft im HauptThread
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  tc:Cardinal;
begin
  FCS.Enter;
  txt := 'Button1';
  tc := GetTickCount;
  while GetTickCount < (tc + 5000) do Application.ProcessMessages;
  Showmessage(txt);
  FCS.Leave;
end;
Das im Thread (also in einem anderen Thread als der HauptThread)
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  inherited;
  FCS2.Enter;
  txt := 'Thread';
  FCS2.Leave;
end;
Das Perverse daran ist, das funktioniert tatsächlich ... aber das hier würde auch funktionieren:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  tc:Cardinal;
begin
  txt := 'Button1';
  tc := GetTickCount;
  while GetTickCount < (tc + 5000) do Application.ProcessMessages;
  Showmessage(txt);
end;

procedure TMyThread.Execute;
begin
  inherited;
  txt := 'Thread';
end;
Erst wenn der HauptThread und der Thread gleichzeitig schreibend/lesend oder schreibend/schreibend auf die Variable txt zugreifen, erst dann und nur dann gibt es einen Zugriffsfehler.

Die CS ist nicht dazu da, dass es überhaupt funktioniert, sondern dass es immer funktioniert.

Allerdings wirst du bei diesem COde es wahrscheinlich niemals eine Zugriffsverletzung bekommen, da der Thread ja nur einen winzigen Moment läuft und genau diesen Zeitpunkt müsstest du treffen, um eine Zugriffsverletzung zu provozieren.
Nach dem Klicken von dem Button für das Starten des Threads, vergeht aber mehr Zeit, bis die Form wieder Eingaben zulässt, als der Thread mit der abarbeitung von Execute benötigt ... darum brauchst du hier gar keine CS, weil der Zustand so gesehen niemals eintrifft :mrgreen:

Bummi 28. Nov 2010 03:19

AW: CriticalSections wie verwenden?
 
dann muss ich allen Beteiligten erst einmal Abbitte leisten. Der Getter/Setter stellt sicher dass ich mich in der richtigen CS befinde, habe ich das richtig verstandenen?

Sir Rufo 28. Nov 2010 03:20

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von Bummi (Beitrag 1064557)
dann muss ich allen Beteiligten erst einmal Abbitte erleisten. Der Getter/Setter stellt sicher dass ich mich in der richtigen CS befinde, habe ich das richtig verstandenen?

jepp :thumb: u got it

Assarbad 28. Nov 2010 03:26

AW: CriticalSections wie verwenden?
 
Genau.

Ich war auch erstmal verblüfft. Aber Kommentar #9 hat dann auch den Sinn hinter den CS pro Thread aufgezeigt. Ab da war's absolut klar.

Tut mir leid wenn ich zur Verwirrung beigetragen haben sollte. Man sollte eben den kompletten Kontext kennen :zwinker:

Bummi 28. Nov 2010 09:16

AW: CriticalSections wie verwenden?
 
@Assarbad

muss Dir nicht Leid tun, ich war komplett vernagelt.

Ich danke nochmals Sir Rufo, Jaenicke und Assbard für ihre Bemühungen.

xZise 28. Nov 2010 15:03

AW: CriticalSections wie verwenden?
 
Könnte man nicht das auch so beschreiben:

In dem „Universum“ gibt es zwei Personen und zwei Häuser. Jede Person stellt ein Thread dar, und jedes Haus dessen Speicherbereich. Eine CriticalSection ist jetzt wie eine Tür:

Wenn jetzt die Person A auf eine Variable der Person B zugreift (z.B. den Stromzählerstand ablesen), dann geht die Person A in das Haus von B und liest den Zählerstand ab. Aber währenddessen kann ja die Person B den Zähler manipulieren. D.h. Solange die Person B daran arbeitet schließt sie die Tür ab (betritt also die CriticalSection) und wenn sie fertig ist, verlässt sie das Haus (bzw. die CriticalSection).

Diese CriticalSection befindet sich dabei im Haus B (ist ja auch die Tür von Haus B). Solange kommt die Person A (oder jemand anderes) ran.

MfG
Fabian

SneakyBagels 21. Mai 2017 17:22

AW: CriticalSections wie verwenden?
 
Um von einem anderen Thema wegzukommen in welchem es ein bisschen ins OffTopic bzgl CriticalSections ging knüpfe ich hier mit einer Frage an.

Angenommen ich erstelle 2 oder mehr Threads, die alle ein und das gleiche tun nur jeweils eine eigene Liste mit bestimmten Informationen haben.
Zur Anzeige schickt jeder Thread eine Message an einen anderen Thread (um diese Zeile Code ist aktuell ein CriticalSectionLocal.Enter und Leave drum), welcher ein Label ändert (diesen Thread gibt es nur 1x). In diesem GUI-Thread hole ich die Message mit GetMessage.
Soweit erstmal Horror-Code #1, ist mir aber egal und darauf BITTE nicht eingehen!

Die Frage lautet nun: bringt es irgendetwas eine globale CriticalSection VOR der Erstellung der 2 oder mehr Threads zu betreten und am Ende aller Arbeit wieder zu verlassen?
Somit würde ich doch jeder der 2+ Threads in dieser einen, globalen Critical Section befinden.
Oder sollte jeder Thread seine eigene, lokale CriticalSection haben? Daraus bin ich bis hier hin leider noch nicht schlau geworden.
Außerdem... braucht man mit einer CriticalSection Synchronize(procedure bla.Caption := '123'; end); innerhalb eines Threads noch?

Das Thema scheint wohl öfter aufzukommen ob mehrere lokal oder eine global: http://www.delphipraxis.net/170736-m...alsection.html

Olli73 21. Mai 2017 18:13

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1372244)
Angenommen ich erstelle 2 oder mehr Threads, die alle ein und das gleiche tun nur jeweils eine eigene Liste mit bestimmten Informationen haben.
Zur Anzeige schickt jeder Thread eine Message an einen anderen Thread (um diese Zeile Code ist aktuell ein CriticalSectionLocal.Enter und Leave drum), welcher ein Label ändert (diesen Thread gibt es nur 1x). In diesem GUI-Thread hole ich die Message mit GetMessage.
Soweit erstmal Horror-Code #1, ist mir aber egal und darauf BITTE nicht eingehen!

Die Frage lautet nun: bringt es irgendetwas eine globale CriticalSection VOR der Erstellung der 2 oder mehr Threads zu betreten und am Ende aller Arbeit wieder zu verlassen?
Somit würde ich doch jeder der 2+ Threads in dieser einen, globalen Critical Section befinden.
Oder sollte jeder Thread seine eigene, lokale CriticalSection haben? Daraus bin ich bis hier hin leider noch nicht schlau geworden.
Außerdem... braucht man mit einer CriticalSection Synchronize(procedure bla.Caption := '123'; end); innerhalb eines Threads noch?

Die CriticalSection um das ganze herum würde nur verhindern, dass das Ganze (Erstellern von 2+ Threads und abarbeiten) nicht ein zweites mal gleichzeitig gestartet werden kann.

Mit einer CriticalSection sperrst du eine globale Ressource (z.B.: globale Variable) für den Zugriff durch andere. Wenn du fertig bist, gibst du den Zugriff wieder frei. Wer zwischendurch drauf zugreifen will, muss warten. Stelle es dir einfach wie eine Toilette (= Ressource, die man i.d.R. alleine benutzen möchte) vor, das Türschloss ist die CriticalSection. Damit stellt man sicher, dass man alleine ist. Natürlich muss jeder, der die Toilette betritt, auch das Türschloss benutzen, sonst funktioniert es nicht (es darf auch keiner durch das Fenster einsteigen). Du hast in deinem Beispiel übrigens mehrere Leute in der Toilette eingeschlossen, und wartest bis alle Fertig sind, um sie wieder rauszulassen. Dabei sind sogar alle gleichzeitig am ******, was für eine Sauerei.

Wenn du allerdings auf VCL-Komponenten zugreifen willst brauchst du zwingend ein Synchronize oder du verschickst eine Message (SendMessage / PostMessage).

SneakyBagels 21. Mai 2017 18:20

AW: CriticalSections wie verwenden?
 
Heißt also auch eigentlich, dass eine lokale im Thread selbst erzeugte CriticalSection genau so sinnfrei ist wie eine die um den ganzen Code drum rum ist.

Olli73 21. Mai 2017 18:22

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1372247)
Heißt also auch eigentlich, dass eine lokale im Thread selbst erzeugte CriticalSection genau so sinnfrei ist wie eine die um den ganzen Code drum rum ist.

Genau.

SneakyBagels 21. Mai 2017 18:27

AW: CriticalSections wie verwenden?
 
Das kam mir gleich sehr seltsam vor mit der lokalen CriticalSection. Denn wie soll Thread1 denn von der CriticalSection von Thread2 erfahren? Also ist das eher Quatsch.
Gut dann habe ich das wenigstens umgestellt und werde mir merken:

WENN (!) es nicht anders geht, dann Synchronize für GUI
und schreiben (nicht lesen?) von globalen Variablen nur mit globaler CriticalSection ("global"... es ist eine Unit mit Records und Classes).

Olli73 21. Mai 2017 19:07

AW: CriticalSections wie verwenden?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1372250)
und schreiben (nicht lesen?) von globalen Variablen nur mit globaler CriticalSection

Wenn wirklich nur (!!!) gelesen wird, brauchst du nichts abzusichern. Wird aber irgendwo möglicherweise geschrieben, musst du auch die Lesezugriffe absichern.

Für viele Lese- und wenige Schreibzugriffe gibt es auch noch TMultiReadExclusiveWriteSynchronizer

SneakyBagels 21. Mai 2017 19:29

AW: CriticalSections wie verwenden?
 
Zitat:

Wenn der Schutz des globalen Speichers in einem kritischen Abschnitt implementiert wird, kann immer nur ein Thread auf den geschützten Speicher zugreifen.
Ist das nur ein wenig komisch formuliert oder ist das wirklich so?
Dem Zitat oben entnehme ich, dass eine normale CriticalSection auch anderen Threads den nur-lesen-Zugriff nicht erlaubt? Bitte sag mir, dass ich falsch liege.

Thread1 geht in Prozedur XYZ > CriticalSection.Enter > setzen von globaler Variable 123 > CriticalSection.Leave
Währenddessen kann Thread2 nicht lesen auf globale Variable 123 zugreifen?

Olli73 21. Mai 2017 19:36

AW: CriticalSections wie verwenden?
 
Während eines Schreibzugriffes darf nicht gelesen werden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:54 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