![]() |
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 |
AW: TIniFile und Terminal-Server
Dafür nimmt man einen Mutex.
Aber das Global\<MutexName> nicht vergessen ;) |
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. |
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. |
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 |
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:
Dann geht natürlich die Ändeung von 2 verloren.
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 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. |
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:
oder
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;
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; |
AW: TIniFile und Terminal-Server
Und dann kannst du das mal mit dieser von
Delphi-Quellcode:
abgeleiteten Klasse versuchen :)
TIniFile
Bei jedem Zugriff auf die Datei wird erst der Mutex betreten und danach wieder verlassen.
Delphi-Quellcode:
Die Hash-Routine für den Dateinamen ist eine sehr einfache und kann bei Bedarf auch gewechselt werden.
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. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:52 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