Filestream.Lock: Rätselhaftes Verhalten
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:
Die ominöse Exception tritt beim ersten Reader.ReadByte in N_Lesen auf, nachdem das vorangegangene N_Lock(i) problemlos durchgegangen war.
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; Ich hoffe, ich habe mich verständlich ausgedrückt, sonst bitte nachfragen! Grüße Lothar |
Re: Filestream.Lock: Rätselhaftes Verhalten
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)???
|
Re: Filestream.Lock: Rätselhaftes Verhalten
@SirT: Siehst du irgendwo einen Konstruktoraufruf von TFileStream :zwinker: ?
@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 :mrgreen: . |
Re: Filestream.Lock: Rätselhaftes Verhalten
wo findet sich FileStream? Habs weder in der Hilfe noch per Codevervollständigung gefunden.
|
Re: Filestream.Lock: Rätselhaftes Verhalten
System.IO.FileStream, Assembly mscorlib, .Net Base Class Library
|
Re: Filestream.Lock: Rätselhaftes Verhalten
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. :gruebel: Greetz alcaeus |
Re: Filestream.Lock: Rätselhaftes Verhalten
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 |
Re: Filestream.Lock: Rätselhaftes Verhalten
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 |
Re: Filestream.Lock: Rätselhaftes Verhalten
... 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 |
Re: Filestream.Lock: Rätselhaftes Verhalten
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 |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:53 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