AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Prism Filestream.Lock: Rätselhaftes Verhalten
Thema durchsuchen
Ansicht
Themen-Optionen

Filestream.Lock: Rätselhaftes Verhalten

Offene Frage von "Lothar"
Ein Thema von Lothar · begonnen am 30. Apr 2007 · letzter Beitrag vom 30. Apr 2007
Antwort Antwort
Seite 1 von 2  1 2      
Lothar

Registriert seit: 26. Apr 2007
Ort: München
12 Beiträge
 
Turbo Delphi für Win32
 
#1

Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 16:59
Hallo!

Ich kämpfe seit 2 Tagen mit einem Problem beim Filelocking, für das ich keine Lösung finden kann. Um die Sache klarer zu machen, habe ich alles auf ein einfaches Testprogramm reduziert, wo ich die Schwierigkeit lokalisieren kann:

Ich schreibe z.B. 20.000 Mal 8 Bytes hintereinander in eine neue Datei. Dann setze ich einen Lock auf irgendeinen Datenbereich dieser 8 Bytes, z.B. beim 1000-sten Satz. Wenn ich dann mit einem zweiten Aufruf dieses Programms (Parallelausführung) auf diese 8 Bytes mit "Lesen_iClick" zugreife (oder auch mit derselben Instanz), kommt genau das erwartete Verhalten: es wird nämlich gemeldet, daß der Bereich gelocked ist. Kurioserweise bekomme ich aber bei den darunterliegenden Daten bei "Lesen_iClick" eine IOException, obwohl dort gar nichts gelocked ist! Und zwar bis zu einem Bereich von exakt Satz-Nr. 489, also ca. 4096 Bytes unterhalb; ansonsten kann ich normale Lesezugriffe machen (also vom Dateistart an bis zu diesem kritischen Bereich, also bis Satz-Nr. 488, und außerdem direkt oberhalb dem gelockten Bereich, also ab Satz 1001).

Die IOException würde ja bedeuten, daß etwas gesperrt ist, aber es ist eben NICHTS gesperrt. Trotz aller Versuche kriege ich den Fehler nicht weg! Ich wäre daher für jede Art Hilfe äußerst dankbar.

Hier Teile aus der Source:

Delphi-Quellcode:
type
   NRec = record
      a,b,c,d,e,f,g,h: byte;
   end;

   private
      N_recsize: integer;
      N_Datei: FileStream;
      procedure N_Close;
      function N_OpenCreate: boolean;
      function N_Lock (i:integer): boolean;
      function N_UnLock (i:integer): boolean;
      function N_Lesen (i:integer; var NSatz:NRec{a,b,c,d,e,f,g,h: byte}): boolean;
      procedure N_Locktest (i:integer; bLock:boolean);

...


procedure THaupt2F.N_Close;
begin
   try
      N_Datei.Free;
   except
      Msg('# N_close');
   end;
end;

function THaupt2F.N_OpenCreate: boolean;
begin
   result:=TRUE;
   try
      N_Datei:=Filestream.Create('test.dat',
         FileMode.OpenOrCreate,
         FileAccess.ReadWrite,FileShare.ReadWrite);
   except
      Msg('#OpenorCreate');
      result:=FALSE;
   end;
   N_recsize:=8;
end;

function THaupt2F.N_Lock (i:integer): boolean;
begin
   result:=TRUE;
   try
      N_Datei.Lock(i*N_recsize,1{N_recsize}); {-> der Einfachheit halber nur 1 Byte gelocked}
   except
      Msg(inttostr(i)+' schon locked');
      result:=FALSE;
   end
end;

function THaupt2F.N_UnLock (i:integer): boolean;
begin
   result:=TRUE;
   try
      N_Datei.UnLock(i*N_recsize,1{N_recsize}); {-> s.o.}
   except
      Msg('Unlock '+inttostr(i)+' fail');
      result:=FALSE;
   end;
end;

procedure THaupt2F.AnlegenSchreibenClick(Sender: TObject);
var i,j: integer;
      Writer: BinaryWriter;
      Buffer: array [1..8] of byte;
begin
   if NOT N_OpenCreate then exit;

   Writer:=BinaryWriter.Create(N_Datei);

   for i:=0 to 19999 do
      begin
         for j:=1 to 7 do Buffer[j]:=0; Buffer[8]:=i; { 1 Eintrag }
         Writer.Write(Buffer);
      end;

   N_Close;
end;

function THaupt2F.N_Lesen (i:integer; var NSatz:NRec{a,b,c,d,e,f,g,h:byte}): boolean;
var Reader: BinaryReader;
begin
   result:=TRUE;

   Reader:=BinaryReader.Create(N_Datei);

   with NSatz do
   try
      N_Datei.Seek(i*N_recsize,SeekOrigin.Begin);

      a:=Reader.ReadByte;   b:=Reader.ReadByte;
      c:=Reader.ReadByte;   d:=Reader.ReadByte;
      e:=Reader.ReadByte;   f:=Reader.ReadByte;
      g:=Reader.ReadByte;   h:=Reader.ReadByte;
   except
      on IOException do
         begin
            Msg('# IOException');
            result:=FALSE;
         end;
   end;
end;

procedure THaupt2F.Lesen_iClick(Sender: TObject);
var i:integer;
      NSatz: NRec; {a,b,c,d,e,f,g,h: byte;}
begin
   AListe.clear;

   try
      i:=strtoint(Edit1.Text);
   except
      exit
   end;

   if NOT N_OpenCreate then exit;

   if N_Lock(i) then
      begin
         if NOT N_Lesen(i,NSatz{a,b,c,d,e,f,g,h}) then
               begin N_close; exit end;
         N_UnLock(i);

         with NSatz do
            AListe.Items.add(a.ToString+' '+
               b.ToString+' '+
               c.ToString+' '+
               d.ToString+' '+
               e.ToString+' '+
               f.ToString+' '+
               g.ToString+' '+
               h.ToString);
      end;
   N_close;
end;

procedure THaupt2F.N_Locktest (i:integer; bLock:boolean);
begin
   if bLock then
      begin
         if NOT N_OpenCreate then exit;
         if N_Lock(i) then;
      end
   else
      begin
         if N_UnLock(i) then;
         N_close;
      end;
end;

...

procedure THaupt2F.Lock1000Click(Sender: TObject);
begin   N_Locktest(1000,TRUE); end;

...

procedure THaupt2F.UnLock1000Click(Sender: TObject);
begin N_Locktest(1000,FALSE); end;
Die ominöse Exception tritt beim ersten Reader.ReadByte in N_Lesen auf, nachdem das vorangegangene N_Lock(i) problemlos durchgegangen war.

Ich hoffe, ich habe mich verständlich ausgedrückt, sonst bitte nachfragen!

Grüße
Lothar
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 17:33
Das es sich um .Net handelt ist korrekt oder hast du dich da vertippt? Kann es sein das TFileStream von sich aus die Methode Lock gar nicht hat (zumindest unter win32 nicht)???
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#3

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 17:49
@SirT: Siehst du irgendwo einen Konstruktoraufruf von TFileStream ?

@Lothar: 4096 Byte ist exakt die Standardgröße des FileStream-Buffers, das kann kein Zufall sein. Auch wenn du ein paar Byte unter der gelockten Adresse schreiben willst, wird der Stream wohl(nicht gerade mein Fachgebiet) den ganzen Buffer beim Flush auf die Festplatte schreiben, was logischerweise nicht problemlos verlaufen wird.

PS: Mir sind schon viele lustige Member-Präfixe begegnet, aber "N_" ist mal was Neues .
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#4

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 18:10
wo findet sich FileStream? Habs weder in der Hilfe noch per Codevervollständigung gefunden.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#5

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 18:23
System.IO.FileStream, Assembly mscorlib, .Net Base Class Library
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
Benutzerbild von alcaeus
alcaeus

Registriert seit: 11. Aug 2003
Ort: München
6.537 Beiträge
 
#6

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 18:52
Moin,

schreibt FileStream direkt auf die Platte? In dem Fall wuerde ich davon ausgehn, dass der gesamte Cluster gesperrt wird, in dem der zu sperrende Bereich liegt.

Greetz
alcaeus
Andreas B.
Die Mutter der Dummen ist immer schwanger.
Ein Portal für Informatik-Studenten: www.infler.de
  Mit Zitat antworten Zitat
Lothar

Registriert seit: 26. Apr 2007
Ort: München
12 Beiträge
 
Turbo Delphi für Win32
 
#7

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 19:32
Hallo!

Danke für die Tips und Hinweise, aber das hat mich noch nicht weitergebracht.
Daß das Programm so wunderbar funktioniert und nur genau den betreffenden Satz sperrt (wie auch in der .NET-Anleitung beschrieben - dazu gibt es ja auch Beispiele), habe ich bereits erwähnt. Ominös ist nur, daß in diesem Bereich "unterhalb" der gelockten Sektion systematische Lesefehler auftreten. Das ist aber nirgendwo beschrieben oder erwähnt und entspricht auch keinesfalls der .NET-Dokumentation. Von "Clustern" ist da keine Rede. Also entweder das Framework ist hier fehlerhaft oder ich habe falsch programmiert. Die Antwort darauf würde mich aber brennend interessieren, weil ich auf die Funktion dringend angewiesen bin.

Gruß
Lothar
  Mit Zitat antworten Zitat
Lothar

Registriert seit: 26. Apr 2007
Ort: München
12 Beiträge
 
Turbo Delphi für Win32
 
#8

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 20:40
Hallo!

Ok - das mit den 4096 Bytes stimmt! Ich habe spaßeshalber in meinem Beispielprogramm die Satzlänge von 8 auf 4096 Bytes erhöht. Diesmal läuft das Program ohne jeden Fehler. Verkürze ich die Satzlänge aber auf 4095 Bytes, so kommt die Exception (bei gelocktem Satz 1000) nur noch im Satz 999.

Nur hilft mir das auch nicht weiter, denn ich benutze nur Dateien mit kleineren Satzlängen - und was mache ich dann da?
Außerdem finde ich es schon seltsam, daß das nirgendwo in der MSDN-Bibliothek erwähnt wird.

Lothar
  Mit Zitat antworten Zitat
bit4bit

Registriert seit: 14. Jun 2006
Ort: Köln
25 Beiträge
 
#9

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 22:02
... mal ne grundsätzliche Überlegung...

Record-Locking soll doch normalerweise verhindern, dass die Datensätze einer Datei inkonsistent werden. Dazu ist es notwendig, dass "mindestens" die gewünschten Daten gesperrt werden (mehr Daten dürfen jederzeit gesperrt werden).

Ist die Transaktion beendet, sollten die gesperrten Daten sofort wieder freigegeben werden.

Jedes Programm muss also darauf gefasst sein, dass ein Zugriff scheitert und damit auch so umgehen können, dass keine Deadlock-Situation eintritt.

-- Übrigens konnte ich nirgendwo finden, dass "nur" die gelockten Bytes gesperrt werden, wo hast Du das gefunden?

Das Lock muss an der Stelle problemlos durchgehen, sonst würdest Du ja Deine Reservierung nicht anmelden können.
Denk mal an "first come - first served".

Mit Clustern hat das Ganze übrigens IMHO nichts zu tun, sonder mit der Buffer-Size, wie Khabarakh schon sagte.

Sag doch mal bitte warum Dich dieses Verhalten eigentlich so behindert.

Gruß, bit4bit
  Mit Zitat antworten Zitat
Lothar

Registriert seit: 26. Apr 2007
Ort: München
12 Beiträge
 
Turbo Delphi für Win32
 
#10

Re: Filestream.Lock: Rätselhaftes Verhalten

  Alt 30. Apr 2007, 22:59
Hallo!

Danke für die Nachfragen!
Es ist ganz einfach: Ich teste gerade eine Portierung einer Win32-Anwendung. In Win32 gibt es "file of (record)". Da ist es ganz simpel: Wie Du sagst wird recordweise gelocked. Bei .NET gibt es aber kein "file of (record)" mehr, sondern nur noch Filestreams. D.h. auf die Datei wird byteweise sukzessive zugegriffen. Mit 'seek' oder 'position' setzt man an der Stelle, wo der Record ist, auf und liest dann den entsprechenden Bereich. Man muß also den Start ausrechnen und die Länge des Records lesen - bzw. hier: locken.

Selbstverständlich soll aber nur der Bereich des Record gelocked werden und nichts sonst. Der wird hier auch sauber gesperrt, aber was dann passiert, ist, daß ein darunter/davorliegender Bereich von ca. 4 K nicht mehr lesbar ist (es kommt die besagte Exception). Das ist natürlich indiskutabel.

Ich habe mir in den letzten Tagen alle im Web auffindbaren Beispiele von Filestream.Lock angeschaut. Da steht es auch entsprechend. Aber von dem Lesefehler steht nirgends etwas erwähnt.

Ich benutze übrigens mit Win32 auch schon seit längerem TFilestream. Da geht das Locking über

LockFile(hZei,Recordnumber * sizeof(NSatz),0,sizeof(NSatz),0);
[wobei hZei:=FileOpen(N_Name,fmReadWr+fmDenyNone);]


also im Grunde analog, mur mit Datei-Handle. Und das Lesen geht mit

FileSeek(hZei,Recordnumber * sizeof(NSatz),0); und
FileRead(hZei,NSatz,SizeOf(NSatz));


Der riesige Vorteil ist hier, daß ich mit einem Befehl den ganzen Record auslesen kann, mit der ganzen heterogenen Datenstruktur. In .NET muß ich aber byte- und char-weise hintereinander auslesen. Aber das nur nebenbei...

Gruß
Lothar
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 17:49 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