Zugriffe auf Datenstruktur synchronisieren

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von Warf »

braunbär hat geschrieben:
Der assembler hat nichts zu sagen, critical sections sind per definition atomar, während boolean Felder auf Compiler und Architektur ankommen.
Mit der neuen Fpc version könnte das schon ganz anders aussehen. Bei nicht definiertem verhalten darf man niemals Annahmen über das Verhalten machen.

Zum einen würde mich ein Link dazu interessieren.
Zum anderen wäre das in diesem Fall auch egal, weil auf der einen Seite nur gelesen, und auf der anderen nur geschrieben wird.
Entweder das Schreiben passiert "rechtzeitig", dann wird der Click in der aktuellen Schleife verarbeitet, oder es kommt zu spät, dann eben in der nächsten Schleife. Passieren kann nichts. Nebenbei würde ich die Wahrscheinlichkeit, dass in einer zukünftigen FPC Version das atomare movb bzw. cmpb durch irgend etwas anderes ersetzt wird, auf eine glatte Null einschätzen.

Delphi produziert hier übrigens auch ganz atomaren Code. Zumindest Delphi 5 und das RAD Studio 10.1 Berlin. Turbo Pascal 3 ebenfalls. :D
Die Versionen dazwischen habe ich nicht gecheckt.

PS: critical sections machen übrigens ein Check und set in einer Aktion, also ein der Wert wird ausgelesen, ein neuer wert wird geschrieben, in einem Tick. Ohne diese atomare Eigenschaft kann zwischen dem get und dem set unterbrochen werden, und dann denken beide Threads die Variable wäre nicht gesetzt und führen gleichzeitig Code aus. Mit critical sections ist das per Definition unmöglich, unabhängig von compilerverhalten, Optimierung oder Architektur

Das ist schon klar, hat aber mit der Aufgabenstellung hier nichts zu tun.


Tut mir leid, ich hatte gestern wohl ein paar Bierchen zu viel, und habe irgendwie deinen ersten Post mit dem von wp zusammen gelesen, und dachte du wölltest das über islocked lösen, dass ist nämlich alles andere als atomar.

Deine Lösung funktioniert so, allerhöchstens könnte es dazu führen das mit der Aktion ein kompletter Zyklus länger gewartet werden muss, aber ansonsten ist sie gut

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von braunbär »

Warf hat geschrieben:Tut mir leid, ich hatte gestern wohl ein paar Bierchen zu viel,

Überhaupt kein Problem, das soll mir angeblich auch schon passiert sein - nicht mit Bier, das ist überhaupt nicht meins, aber Wein oder Cocktails können manchmal verdammt schnell im Glas verdunsten. :)

Braunbär hat geschrieben:Zum einen würde mich ein Link dazu interessieren.

Ist ernst gemeint. Hast du da etwas für mich? Dass derartige Zuweisungen oder Abfragen auf boolean nicht atomar sein könnten, habe ich noch nicht gehört. Bei bitpacked booleans natürlich, und bei kombinierten Lese- und Schreibvorgängen natürlich auch (das betrifft aber jeden Datentyp).

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von braunbär »

Erst Invisible setzen statt zu löschen, und erst im nächsten Durchlauf von Timer1Timer die unsichtbaren Kreise wirklich zu löschen, ist übrigens eine einfache Möglichkeit, die (ohnedies sehr unwahrscheinlichen) Exceptions in der MouseDown Routine zu vermeiden. Weil ein unsichtbares Element kann nicht mehr angeklickt werden, und wenn erst beim nächsten Durchlauf der Timer Routine die unsichtbaren Elemente wirklich gelöscht werden, dann kann dort gar nichts mehr passieren. Das wäre noch besser, als die Exception mittels Try-except zu übergehen.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von Warf »

braunbär hat geschrieben:
Warf hat geschrieben:Tut mir leid, ich hatte gestern wohl ein paar Bierchen zu viel,

Überhaupt kein Problem, das soll mir angeblich auch schon passiert sein - nicht mit Bier, das ist überhaupt nicht meins, aber Wein oder Cocktails können manchmal verdammt schnell im Glas verdunsten. :)

Braunbär hat geschrieben:Zum einen würde mich ein Link dazu interessieren.

Ist ernst gemeint. Hast du da etwas für mich? Dass derartige Zuweisungen oder Abfragen auf boolean nicht atomar sein könnten, habe ich noch nicht gehört. Bei bitpacked booleans natürlich, und bei kombinierten Lese- und Schreibvorgängen natürlich auch (das betrifft aber jeden Datentyp).


Zu dem Link, das hatte ich vor nicht allzulanger Zeit in der Uni gelernt (Modul Betriebssysteme) daher kann ich dir dazu keinen Link geben, da alles was ich hatte das Skript von Professor ist. Und da stand einfach nur drin, normalerweise verwenden selbstgebaute schaltmechanismen (wie das lock von wp) zunächst eine Abfrage, und dann ein schreiben

Code: Alles auswählen

if not locked then
  Locked := true


Also 2 Anweisungen. Für critical sections stellt das OS normalerweise semaphores bereit, welche entweder einen checkandset Kommando ausführen (auf CISC) oder auf Risc Prozessoren, die keine eigene instruction dafür kennen, wird dann während diesen 2 instructions einfach kurz interrupts deaktiviert.

Somit ist gewährleistet dass zwischen diesen beiden instructions nie unterbrochen wird.

Das sollte auch irgendwo in der Linux Man stehen, finde es auf die schnelle aber grade nicht

Zu der Check and set habe ich aber zumindest mal das gefunden:
https://en.m.wikipedia.org/wiki/Test-an ... prov=sfti1

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von braunbär »

Ok, dann ist es mir klarer und bezieht sich nicht speziell auf boolean.
Du hast natürlich Recht, dass für das sichere wechselseitige Locking asynchroner Prozesse Semaphore in irgend einer Form unabdingbar sind.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von Warf »

braunbär hat geschrieben:Ok, dann ist es mir klarer und bezieht sich nicht speziell auf boolean.
Du hast natürlich Recht, dass für das sichere wechselseitige Locking asynchroner Prozesse Semaphore in irgend einer Form unabdingbar sind.


Jap, auch wenn es hier nicht nötig wäre, finde ich man sollte sich das arbeiten mit critical sections auch bei so einfachen Aufgaben angewöhnen, und solche locks nicht selbst implementieren

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von Mathias »

Ich hätte noch eine Anregung für einen besseren Bild-Lauf.

Bei Ramdomize würde ich noch ein

Code: Alles auswählen

  DoubleBuffered := True;
  Randomize;     

einfügen, so flimmert das Bild weniger.

Auch würde ich den Timer auf 40ms setzen, dafür aber auch RtgX und RtgY dem entsprechend kleiner wählen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mintpc
Beiträge: 124
Registriert: Mo 6. Sep 2010, 18:39
OS, Lazarus, FPC: Win 7 (L 1.6 FPC 3.0.0)
CPU-Target: Win 7
Wohnort: Mailand

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von mintpc »

Vielen Dank für die Hilfen, besonders an Braunbär, die Tipps hab ich aufgenommen und umgesetzt.

Was mich noch interessieren würde, sind diese "critical sections", die Warf erwähnt. Wo kann ich mich denn da mal genauer schlau machen? Gibts da gute Literatur zu?

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von Warf »

mintpc hat geschrieben:Vielen Dank für die Hilfen, besonders an Braunbär, die Tipps hab ich aufgenommen und umgesetzt.

Was mich noch interessieren würde, sind diese "critical sections", die Warf erwähnt. Wo kann ich mich denn da mal genauer schlau machen? Gibts da gute Literatur zu?


http://wiki.freepascal.org/Multithreade ... l_sections

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Zugriffe auf Datenstruktur synchronisieren

Beitrag von mse »

braunbär hat geschrieben:
Warf hat geschrieben:Verwende kein Boolean Feld. Zugriffe auf booleanfelder sind nicht atomar.

Wie kommst du darauf?
boolean.PNG

Viel atomarer als ein movb oder ein cmpb (s.Anhang) geht nicht. Ob auf der untersten Hardwareebene ein paar zusätzliche Taktzyklen gebraucht werden, um ein einzelnes Byte zu schreiben (beim lesen nicht einmal das), ist für den Programmablauf egal. Da fährt kein Interrupt dazwischen.

Darauf kann man sich bei Mehrkernprozessoren nicht mehr verlassen. Nicht unterbrechbare Operationen gibt es in Free Pascal in der unit system:

Code: Alles auswählen

 
function InterLockedIncrement (var Target: longint) : longint; public name 'FPC_INTERLOCKEDINCREMENT';
function InterLockedDecrement (var Target: longint) : longint; public name 'FPC_INTERLOCKEDDECREMENT';
function InterLockedExchange (var Target: longint;Source : longint) : longint; public name 'FPC_INTERLOCKEDEXCHANGE';
function InterLockedExchangeAdd (var Target: longint;Source : longint) : longint; public name 'FPC_INTERLOCKEDEXCHANGEADD';
function InterlockedCompareExchange(var Target: longint; NewValue: longint; Comperand: longint): longint; public name 'FPC_INTERLOCKEDCOMPAREEXCHANGE';
{$ifdef cpu64}
function InterLockedIncrement64 (var Target: int64) : int64; public name 'FPC_INTERLOCKEDINCREMENT64';
function InterLockedDecrement64 (var Target: int64) : int64; public name 'FPC_INTERLOCKEDDECREMENT64';
function InterLockedExchange64 (var Target: int64;Source : int64) : int64; public name 'FPC_INTERLOCKEDEXCHANGE64';
function InterLockedExchangeAdd64 (var Target: int64;Source : int64) : int64; public name 'FPC_INTERLOCKEDEXCHANGEADD64';
function InterlockedCompareExchange64(var Target: int64; NewValue: int64; Comperand: int64): int64; public name 'FPC_INTERLOCKEDCOMPAREEXCHANGE64';
{$endif cpu64}
{ Pointer overloads }
{$ifdef cpu64}
function InterLockedIncrement (var Target: Pointer) : Pointer; external name 'FPC_INTERLOCKEDINCREMENT64';
function InterLockedDecrement (var Target: Pointer) : Pointer; external name 'FPC_INTERLOCKEDDECREMENT64';
function InterLockedExchange (var Target: Pointer;Source : Pointer) : Pointer; external name 'FPC_INTERLOCKEDEXCHANGE64';
function InterLockedExchangeAdd (var Target: Pointer;Source : Pointer) : Pointer; external name 'FPC_INTERLOCKEDEXCHANGEADD64';
function InterlockedCompareExchange(var Target: Pointer; NewValue: Pointer; Comperand: Pointer): Pointer; external name 'FPC_INTERLOCKEDCOMPAREEXCHANGE64';
{$else cpu64}
function InterLockedIncrement (var Target: Pointer) : Pointer; external name 'FPC_INTERLOCKEDINCREMENT';
function InterLockedDecrement (var Target: Pointer) : Pointer; external name 'FPC_INTERLOCKEDDECREMENT';
function InterLockedExchange (var Target: Pointer;Source : Pointer) : Pointer; external name 'FPC_INTERLOCKEDEXCHANGE';
function InterLockedExchangeAdd (var Target: Pointer;Source : Pointer) : Pointer; external name 'FPC_INTERLOCKEDEXCHANGEADD';
function InterlockedCompareExchange(var Target: Pointer; NewValue: Pointer; Comperand: Pointer): Pointer; external name 'FPC_INTERLOCKEDCOMPAREEXCHANGE';
{$endif cpu64}
{ unsigned overloads }
function InterLockedIncrement (var Target: cardinal) : cardinal; external name 'FPC_INTERLOCKEDINCREMENT';
function InterLockedDecrement (var Target: cardinal) : cardinal; external name 'FPC_INTERLOCKEDDECREMENT';
function InterLockedExchange (var Target: cardinal;Source : cardinal) : cardinal; external name 'FPC_INTERLOCKEDEXCHANGE';
function InterLockedExchangeAdd (var Target: cardinal;Source : cardinal) : cardinal; external name 'FPC_INTERLOCKEDEXCHANGEADD';
function InterlockedCompareExchange(var Target: cardinal; NewValue: cardinal; Comperand: cardinal): cardinal; external name 'FPC_INTERLOCKEDCOMPAREEXCHANGE';
{$ifdef cpu64}
function InterLockedIncrement64 (var Target: qword) : qword; external name 'FPC_INTERLOCKEDINCREMENT64';
function InterLockedDecrement64 (var Target: qword) : qword; external name 'FPC_INTERLOCKEDDECREMENT64';
function InterLockedExchange64 (var Target: qword;Source : qword) : qword; external name 'FPC_INTERLOCKEDEXCHANGE64';
function InterLockedExchangeAdd64 (var Target: qword;Source : qword) : qword; external name 'FPC_INTERLOCKEDEXCHANGEADD64';
function InterlockedCompareExchange64(var Target: qword; NewValue: qword; Comperand: qword): int64; external name 'FPC_INTERLOCKEDCOMPAREEXCHANGE64';
{$endif cpu64}
 

Da im Beispiel AFAIK keine threads und mehrere Prozesse vorkommen, ist das Problem eher in der Listenverwaltung zu suchen wie braunbär schreibt.

Antworten