Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   TIniFile und Terminal-Server (https://www.delphipraxis.net/177582-tinifile-und-terminal-server.html)

hoika 14. Nov 2013 16:22

TIniFile und Terminal-Server
 
Hallo #,

nicht hauen, ist halt eine "legacy app".
Ich habe hier ein Programm,
was in eine zentrale Ini-Datei einige Einstellungen schreibt.
Die Datei liegt an einer zentral zugänglichen Stelle (z.B. C:\Data).

Jetzt soll dieses Programm auf einem Terminal-Server (TS) laufen.
Wenn jetzt 10 Nutzer das Programm parallel starten und "wild in der Ini rumschreiben",
kann das zu Problemen führen ?

Oder anders gesagt:
Synchronisiert der TS Ini-Zugriffe, vor allem beim Schreiben ?


Bitte keine Sprüche, wie "das macht man nicht mit Inis", "falscher Pfad".
Ist alles bekannt, wird bei Gelegenheit auch umgestellt,
geht aber nicht von heute auf gestern.


Danke

Heiko

Sir Rufo 14. Nov 2013 16:30

AW: TIniFile und Terminal-Server
 
Dafür nimmt man einen Mutex.

Aber das Global\<MutexName> nicht vergessen ;)

himitsu 14. Nov 2013 16:40

AW: TIniFile und Terminal-Server
 
TIniFile synchronisiert rein garnichts, ABER

Jeder einzelne Schreibzugriff auf die INI, also jedes einzelne WriteInteger usw. liest diese Datei erneut aus, händert den Wert und schreibt die Datei sofort zurück.



Globale Einstellungen sollten aber auch global sein und sowas sollte sich eigentlich selten ändern.
Einstellungen für die einzelnen Instanzen sollten besser auch in einzelnen Dateien landen und schon kommt sich da nix mehr in die Quere.

Ansonsten mußt du schon selber die Schreibzugriffe synchronisieren, bzw. gleichzeitige Zugriffe verhindern.

BUG 14. Nov 2013 17:48

AW: TIniFile und Terminal-Server
 
Wenn du häufig aus der INI-Datei liest, dann kannst du dir die Reader-Writer-Synchronisation anschauen.
Die lässt sich auch mit globalen Synchronisierungsmitteln nachbauen.

hoika 15. Nov 2013 10:36

AW: TIniFile und Terminal-Server
 
Hallo,

danke für die Antworten.

Ich habe die App mal auf einem W2003-Server parallel laufen lassen,
es gab bei den Inis keine Probleme.
Vielleicht bin ich zu langsam ?

Das mit dem Mutex klingt gut ...


Heiko

himitsu 15. Nov 2013 10:57

AW: TIniFile und Terminal-Server
 
Da hier jeder einzelne Zugriff auf die Werte einen Schreibzugriff darstellen und nicht alles vom Create bis zum Free einen gemeinsamen Zugriff darstellt (am Anfang auslesen und erst am Ende alles speichern),

Gehen die Zugriffe recht schnell, aber wenn z.B. zwei WriteInteger fast zur selben Zeit ausgeführt werden

Code:
Programm 1 liest die Datei ein und verändert im Arbeitsspeicher den Inhalt für einen Wert
Programm 2 liest die Datei ein und verändert im Arbeitsspeicher den Inhalt für einen Wert
Programm 1 speichert die Änderungen
Programm 2 speichert die Änderungen
Dann geht natürlich die Ändeung von 2 verloren.

Ich weiß auch nicht wie die Sharing-Rechte bei Lese und Schreibzugriffen von Microsoft geregelt sind, dementsprechend kann es natürlich auch zu einem "Zugriff verweigert" kommen, wenn gleichzeitig auf die Datei zugegriffen wird.

himitsu 15. Nov 2013 11:23

AW: TIniFile und Terminal-Server
 
Einfach mal mehrere Programme/Threads mit diesem Code ausführen und die 10 Minuten warten, ob es knallt.
Delphi-Quellcode:
with TIniFile.Create('text.ini') do
  try
    Ident := 'Ident' + IntToStr(GetCurrentThreadId);
    Start := GetTickCount;
    Errors := 0;
    Counter := 0;
    while GetTickCount - Start < 1000*60*10 do begin // 10 Minuten testen
      WriteInteger('Section', Ident, Counter);
      Value := ReadInteger('Section', Ident, -1);
      if Value <> Counter then
        Inc(Errors);
      Inc(Counter);
    end;
    if Errors <> 0 then
      raise Exception.CreateFmt('%d Errors', [Errors]);
  finally
    Free;
  end;
oder
Delphi-Quellcode:
with TIniFile.Create('text.ini') do
  try
    Ident := 'Ident' + IntToStr(GetCurrentThreadId);
    Start := GetTickCount;
    Value := ReadInteger('Section', Ident, 0); // richtiger Startwert, falls es diese Datei schon gibt.
    Counter := Value;
    while GetTickCount - Start < 1000*60*10 do begin // 10 Minuten testen
      Value := ReadInteger('Section', Ident, 0);
      WriteInteger('Section', Ident, Value + 1);
      Inc(Counter);
      Sleep(Random(100));
    end;
    if Value <> Counter then
      raise Exception.CreateFmt('Error (%d <> %d)', [Value, Counter]);
  finally
    Free;
  end;

Sir Rufo 15. Nov 2013 11:49

AW: TIniFile und Terminal-Server
 
Und dann kannst du das mal mit dieser von
Delphi-Quellcode:
TIniFile
abgeleiteten Klasse versuchen :)
Bei jedem Zugriff auf die Datei wird erst der Mutex betreten und danach wieder verlassen.
Delphi-Quellcode:
unit MutexIniFile;

interface

uses
  System.Classes,
  System.SyncObjs,
  System.SysUtils,
  System.IniFiles;

type
  TMutexIniFile = class( TIniFile )
  private
    FMutex :     TMutex;
    FUpdateLock : Integer;
  protected
    function GetMutexName : string; virtual;
  public
    constructor Create( const FileName : string );
    destructor Destroy; override;

    function ReadString(const Section, Ident, Default: string): string; override;
    procedure WriteString(const Section, Ident, Value: String); override;
    procedure ReadSection(const Section: string; Strings: TStrings); override;
    procedure ReadSections(Strings: TStrings); override;
    procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
    procedure EraseSection(const Section: string); override;
    procedure DeleteKey(const Section, Ident: String); override;
    procedure UpdateFile; override;

    procedure BeginUpdate;
    procedure EndUpdate;
  end;

implementation

{ TMutexIniFile }

procedure TMutexIniFile.BeginUpdate;
begin
  if FUpdateLock = 0
  then
    FMutex.Acquire;
  Inc( FUpdateLock );
end;

constructor TMutexIniFile.Create( const FileName : string );
begin
  inherited;
  FMutex := TMutex.Create( nil, False, 'Global\' + GetMutexName );
end;

procedure TMutexIniFile.DeleteKey( const Section, Ident : string );
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

destructor TMutexIniFile.Destroy;
begin
  while FUpdateLock > 0 do
    EndUpdate;

  FMutex.Free;
  inherited;
end;

procedure TMutexIniFile.EndUpdate;
begin
  Dec( FUpdateLock );
  if FUpdateLock = 0
  then
    FMutex.Release;
end;

procedure TMutexIniFile.EraseSection( const Section : string );
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

function TMutexIniFile.GetMutexName : string;
  function HashOf( const Key : string ) : Cardinal;
  var
    I : Integer;
  begin
    Result := 0;

    for I := 0 to Key.Length - 1 do
      begin
        Result := ( ( Result shl 2 ) or ( Result shr ( SizeOf( Result ) * 8 - 2 ) ) ) xor Ord( Key.Chars[I] );
      end;
  end;

begin
  Result := Format( 'inifile_%.8x', [HashOf( FileName )] );
end;

procedure TMutexIniFile.ReadSection( const Section : string; Strings : TStrings );
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

procedure TMutexIniFile.ReadSections( Strings : TStrings );
begin
  BeginUpdate;
  try
    inherited ReadSections( Strings );
  finally
    EndUpdate;
  end;
end;

procedure TMutexIniFile.ReadSectionValues( const Section : string; Strings : TStrings );
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

function TMutexIniFile.ReadString( const Section, Ident, Default : string ) : string;
begin
  BeginUpdate;
  try
    Result := inherited;
  finally
    EndUpdate;
  end;
end;

procedure TMutexIniFile.UpdateFile;
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

procedure TMutexIniFile.WriteString( const Section, Ident, Value : string );
begin
  BeginUpdate;
  try
    inherited;
  finally
    EndUpdate;
  end;
end;

end.
Die Hash-Routine für den Dateinamen ist eine sehr einfache und kann bei Bedarf auch gewechselt werden.


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