Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Sa Mai 04, 2024 15:17

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: auf dieselbe Datei 2 mal zugreifen?
BeitragVerfasst: So Mär 15, 2009 22:33 
Offline
DGL Member

Registriert: So Jun 01, 2008 16:06
Beiträge: 31
Wohnort: Nürtingen
Es geht um ein Rollenspiel, da gabs schon Threads von mir, nur ich dachte, wenn die allzualt sind und der Titel nichts mehr mit der aktuellen Frage zu tun hat, dann lieber neu fragen.

Ich lade eine Karte. Damit ich immer praktische Häppchen habe, lädt es immer nur die 4 Abschnitte um mich herum. Die Anzeige tut, alles wunderbar.
Das erste Problem war, dass ich auf die selbe Datei quasi doppelt zugegriffen habe, wenn zB der linke obere Abschnitt ein Stück der Map hat, ich nach links laufe und dann ja der rechte obere Abschnitt das selbe Stück Map will. Ich arbeite hier mit Filestreams und da kommen die sich beide in die Quere.
Das ging, in dem ich gesagt habe, bei jedem neuen Ladevorgang (geladen wird ja nur, wenn ich weit genug in eine Richtung laufe) die Streams mit .free freigelassen werden und ich sie dann quasi neu belege. Man sieht beim spielen nicht, dass sich was tut.

Mein Problem sind die umliegenden Felder.
Ich habe jetzt ersteinmal eine Map von 3x3 Abschnitten. Das heißt, sobald ich einen äußeren Teil betrete, lädt er das Nichts. Ich kann das Nichts nicht als Nichts lassen. Hab mir einen abschnitt 0/0 gemacht und gesagt, der soll geladen werden. Aber dann kommen die sich alle in die Quere "Diese Datei wird von einem anderen Prozess verwendet." -.-
Kann ich die Datei irgendwie so einstellen, dass es egal ist, wer sie alles gleichzeitig liest? Die schreiben alle nicht, die lesen nur oô

Eben habe ich es wirklich damit versucht, dass er dann nichts mehr nachlädt und bei mir war eh drin, dass das letzte Tile unendlich wiederholt wird. Aber dann will Delphi das Ding garnicht mehr laden xD

_________________
blub.. ? oo''


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 00:23 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ganz Ganz Ganz wichtig, öffne niemals die gleiche Datei mehrmals, da dies hohe Performanceeinbrüche mit sich bringt und auch nicht die gute Software Engineer Art ist.
Entkople dein Loader mit einem Thread und gibt diesen die Informationen, welche Blöcke er zu laden hat.
Dieser kann dann mit seinem Read-Stream die Datei lesen und lädt die Blöcke nacheinander.
Sobald ein Block fertig geladen ist, dann kann er ein Callback auslösen.
Das dein Prozess die Datei sperrt, liegt daran, dass du diese mit lesen und schreiben offen hast.

Im anderem Thread hast du ja schon deine Blöcke auf Klassenbasis umgestellt, nun rate ich dir, der Basisklasse das Observer Muster zu spendieren.
Ich hab das Muster im wiki noch nicht Eingetragen(gerade auf die Todo gepackt) darum werde ich es mal grob hier erklären.

Das Observer Muster ist sehr gut mit einem Zeitungs Abo zu vergleichen, es gibt ein Abonnenten und einen Zeitungsausträger.
Der Zeitungsausträger ist dafür zuständig die Zeitung zu besorgen und an die Richtigen Abonennten zu zustellen.
Wenn also ein Abonnent sich beim Zeitungsausträger Registriert, dann musst bei der nächsten Ausgabe, der Zeitung, auch dieser eine Ausgabe erhalten.
Erhält nun der Zeitungsausträger die neue Ausgabe, dann liefert er an alle Abonenten, die sich zuvor Registriert haben eine Ausgabe.

Der Zeitungsausträger ist dein Blockloader, dein Abonennt ist deine Blockklasse und diese kann sich beim Blockloader registrieren und entfernen lassen.
Sobald der Blockloader ein Block fertig hat, dann schickt er eine Nachricht an alle registrierten Objekte(auch der Blockklasse).
Was du brauchst ist also eine TList, eine register, unregister methode im Loader und eine Klasse TObserver mit z.B. einer Methode procedure Update(Data:pointer);, welche du dann in deiner Blockklasse einfach überlädst und z.B. die Blockdaten vom Data Parameter in die protected/privaten Variablen deines Objektes kopierst und ein IsDrawable Flag oder ähnliches auf true sätzt.

Wenn das ganze noch zu undurchsichtig ist, sag bescheit, dann kann ich morgen mal nen micro dummy code schreiben.

*edit*
http://svn.linuxprofessionals.org/filedetails.php?repname=karmarama&path=%2Ftrunk%2Finclude%2Fkar_observer.hpp
http://svn.linuxprofessionals.org/filedetails.php?repname=karmarama&path=%2Ftrunk%2Fsrc%2Fkar_observer.cpp
Wenn du ein bischen c++ verstehst, könnte dir mein Observer Code weiter helfen.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 09:21 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Tak also da muss ich dir wiedersprechen. Nur weil man ein und die selbe Datei zwei mal offen hat heißt es noch lange nicht, dass es schlechter Stil ist. Wenn möglich sollte man es natürlich so gut es geht vermeiden. Aber bei Streamformaten etc kann so etwas schon verdammt praktisch sein. Es hat auch nichts mit Lesen oder Schreiben zu tun. Sondern es gibt dafür einen zusätzlichen Parameter beim Erstellen des FileStreams. Dieser gibt an ob und wie die Datei von anderen geöffnet werden kann. In Delphi steht das per default auf exclusiv. Zum Beispiel kann ein Pogramm, welches Musik aus dem Internet streamt, Dateien erzeugen und schreiben die andere Anwendungen aber nur zum Lesen öffnen dürfen. So ist es möglich sich Musik mit einem anderen Programm direkt anzuhören wärend diese noch geschrieben werden. In einer Anwendung kann so etwas durchaus auch Sinnvoll sein, dann müsste man mit seiner einen FileStream Instanz nicht ständig die Position verändern. Sondern macht einfach 2 Instanzen. Einer schreibt immer bedenkenlos ans Ende und die andere Instanz hat ihren eigenen Positionspointer und kann Vorhandenes auslesen.

Sollte man aber mit 2 Threads gleichzeitig etwas lesen bzw schreiben kann das aber in jedem Fall auch zu Problemen führen. Dann führt man indirekt einen Spur zu Spur Zugriffstest bei Festplatten aus. Was dann dazu führt, dass der Rechner rattert wie irre und sich beide Aktionen gegenseitig ausbremsen. Das gilt im übrigen für alle Dateien der gleichen Festplatte. Nicht nur für gleiche Dateien.

Zu deinem Problem. Da würde ich auch so etwas vorschlagen wie Tak. Also eine zentrale Stelle die sich mit dem Lesen der einzelnen Blöcke beschäftigt. Wie kompliziert so etwas sein muss hängt davon ab wie kompliziert du es gestalten möchtest. Im einfachsten Fall genügt einfach nur eine zentrale Klasse die in der Lage ist einen Block zu laden und zurückzuliefern. Wenn die Blöcke übersichtliche Datengrößen haben, dann wäre es vielleicht sogar noch okay diese nicht in einen Thread auszulagern.

Ansonsten könnte so etwas auch in einem anderem Thread gemacht werden. Also man erstellt einen Thread der Aufgaben abarbeitet. Deine Blockleseoperationen wären so eine Aufgabe. Diese würdest du da rein kippen und wenn die abgearbeitet sind würden die sich per Event melden. Aber wie gesagt. Wie kompliziert es werden darf hängt von deinen Wünschen ab. Denn um so komplexer es wird um so mehr Arbeit (Zeit) steckt dahinter und um so Fehleranfälliger kann es auch werden. Nützt ja auch nichts, wenn es die perfekte Lösung ist und dann dadurch deinen Zeitrahmen sprengt.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 15:41 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Okey, mein Gedankengang war vieleicht nicht ganz Detailiert herrunter geschrieben.
Da ich geschrieben habe, dass wenn er eine Datei mit schreib und lesezugriff öffnet, dann denke ich bei Freepacal nun mal an den entsprechenden TFileStream und blockiert dieser, wenn man Schreibzugriff auswählt. Ich dachte eigentlich auch, dass der Fettdruck von Read-Stream klar macht, was ich meine -_- .

Ich meinte speziell den Fall 2mal die Datei im lesemodus offen zu haben, da ja das Laden von Blockdaten gewollt ist und nicht das speichern und laden von irgendwelchen Internet Medien.
Wenn man mehr als ein lesenden Stream auf eine Datei verwendet, dann hat man definitiv was im Software design falsch gemacht, da dies in mehr Speicher- und Taktverbrauch endet, sowie die Optimierungsmöglichkeit beeinträchtigt(man kann z.B. keine sinnvollen Queues mehr ermöglichen).

Was passiert, wenn 2 Streams die gleiche Datei Sequentiell lesen wollen?
Im Normalfall werden Daten Sequentiell gelesen(nacheinander) und das Dateisystem versucht die Daten auch zusammenhängend auf die Platte zu schreiben.
Dies bedeutet, wenn du deine Daten nacheinander liest, geht das sehr schnell, da das Bewegen des Lesekopfes, um zum nächsten Block zu kommen sehr gering ist.
Kommt nun die Anforderung, dass ein Teil weiter am Ende der Datei geladen werden soll(Stream1), zuvor wurde ein Teil am Anfang gelesen(Stream0) und nun soll ein weiterer Teil vom Anfang gelesen werden(Stream0). Dann bedeutet dass, dass der Festplattencontroller unglücklicherweise wirklich zwischen Anfang und Ende hin und her springt und die höchsten Verlustzeiten beim Lesen von der Festplatte sind diese Seektimes. Dieses verhalten tritt bei dem Standard Streams auf, da diese blockierend sind und nicht Asynchron Arbeiten. Hierfür gibt es einen Befehlssatz von Windows(finde den link nicht auf die schnelle) und Linux(libaio), welcher Asynchrones Lesen erlaubt, dass bedeutet man gibt den Auftrag und dank DMA und dem Festplattencontroller wird das ganze optimiert(im Idealfall der 3. Lesebefehl von Stream0 nach dem ersten Lesebefehl von Stream0 ausgeführt und dann erst der 2. von Stream1) und die Abarbeitung von der CPU abgegeben.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 16:50 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Wie FreePascal (unter Linux) so etwas macht weiß ich nicht. Ich weiß nur, dass es unter Delphi bei TFileStream ein Flag gibt, welches die Zugriffsmöglichkeiten kontrolliert. Und das ungeachtet davon ob man ließt, schreibt oder beides macht.

Was beim Lesen passiert ist mir soweit auch klar. Leseoperationen mit DMA sind da sicherlich die beste Wahl. Allerdings da man dort die API benutzten muss wohl auch nicht für jeden geeignet und vielleicht ist der Aufwand auch gar nicht nötig.

Aber auch auf die Gefahr hin mich vollends unbeliebt zu machen (falls das überhaupt noch möglich ist). Ich sehe es trotzdem nicht so, dass es zwingend ein Fehler im Design sein muss. Nur weil man ein und die selbe Datei zwei mal offen hat. Sicher ist das auch nicht die ideale Lösung. Es kommt meiner Meinung nach aber nur auf das Umfeld an. Denn sagen wir mal wir haben 1 Thread in dem Dinge eingelesen werden. Dann haben wir eine Instanz A mit der wir am Ende einer Datei etwas lesen. Und eine Instanz B mit der wir am Anfang etwas lesen. Dadurch, dass dort nur ein Thread existiert werden die Leseoperationen innerhalb des Threads sowieso synchron abgearbeitet. Erst nachdem alles aus Instanz A gelesen wurde wird mit Instanz B fortgefahren. Erst dann muss der Lesekopf neu positioniert werden. Und dann ist der Unterschied zwischen 2 Instanzen oder 1 Instanz und der Positionierung mittels Position praktisch nicht mehr relevant.

Klar. Wenn man abwechselnd jeweils ein Byte aus den Instanzen liest, dann ist das sehr supoptimal und dann sollte man wirklich sein Design überdenken. Gleiches trifft auch zu, wenn man (ohne dma) aus 2+ Threads gleichzeitig von ein und der selben Festplatte ließt. Das endet mit sehr hoher wahrscheinlichkeit in einem reichhaltigen Geratter und deutlich erhöhter Laufzeit. Wenn das für dich absolut falsches Design ist, dann ist das okay. Nur mir genügt es, wenn der Entwickler sich dabei etwas gedacht hat und sich dessen auch bewusst ist was dort passiert.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 17:23 
Offline
DGL Member

Registriert: So Jun 01, 2008 16:06
Beiträge: 31
Wohnort: Nürtingen
Ich hätte natürlich noch die Möglichkeit alle Mapblöcke außerhalb meines Bereichs zu erstellen und per mapwidth/mapheight-angaben bzw eben x/y=0 die Figur nicht außerhalb dieses Bereichs lassen. Dann würden brav die Blöcke außerhalb geladen werden und ich dürfte nicht hin, also würde ich auch keine Probleme mit einer Grenze kriegen und Delphi müsste nicht schreien, dass es -1_-1.map oder so nicht findet.
Halt ich aber irgendwie für blöd.. ka. Ich mag die Idee der unendlichen Grenze, bei denen das letzte Tile wiederholt wird.

Sind es auch 4 Threads gleichzeitig, wenn es einfach nur 4 Vorgänge nacheinander sind? Also 4 aufeinanderfolgende Zeilen? Dann müsste er doch die eine fertigmachen bis er mit der nächsten beginnt?
Ich habe das Programm bisher extrem simpel gehalten (meinem Wissen angepasst) und habe auf sowas bisher wenig geachtet. Ich schau nur, dass ich Rahmen des ganzen die Performance schone und nix unnötiges lade.
Dieselbe Datei bis zu 4 mal zu laden ist natürlich blöd. Ich konnte aber auch keinen Weg finden, Streams zu addieren. Ich dachte erst, es würde wie bei Strings klappen, dass man hinten was anhängt. Dann hätte ich einen Gesamtstream, der alles laden würde und eben den frischgeladenen Abschnitt selbst kopiert und einfügt. Da das nicht wollte, bin ich auf 4 Streams umgestiegen, bei denen dann entschieden wird, welcher grad lesen darf.

Außerdem hatte ich ja so ein schönsimples Konstrukt, bei dem 4 Streams bedient werden müssen. Die laden dann eine Datei, deren Name immer die x- und y-Variablen enthält, die ja wechseln.
Jeder Block ist 32x32 -> 1kb o,o Ich dachte mir, wg 1kb mache ich mir keinen Streß und lade doppelt. Ich arbeite hier mit nem in die Jahre gekommenem Vaio-Subnotebook: Intel Pentium M 1,1GHz, 512MB DDR und ne Intel Xtreme2 oder wie die heißt. Son OnBoard-Teil.
Alle meine Freunde, die jemals mitzocken habn Rechner mit mehr Power, also teste ich bei mir, ob ich einen sichtbaren Ruckler merk und wenn ned, dann gilt die Methode als Tauglich. Das war bisher bei allen Sachen so ^^

Wie gesagt, ich weiß nicht, obs sich wegen 1kb wiederholten Daten bzw 4kb gesamt lohnt, zumals nur um den Randteil geht, den keiner braucht xD

Aber vielen Dank, ich versuch mich da mal reinzuarbeiten. Bin grad froh über alles Neue, was ich seh, weil ich ab dem Wintersemester Informatik studieren will und wenigstens eine Sprache breitflächiger anschaun will...

_________________
blub.. ? oo''


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Mär 16, 2009 18:11 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Wenn du die Daten im Hintergrund laden willst und dein Spiel dabei noch weiter laufen soll, dann reicht ein Thread vollkommen aus, um mit den standard Streamklassen von welcher Sprache/compiler auch immer zu arbeiten. Du übergibst in deinem Hauptprogramm einfach nur der entsprechenden Klasse den Befehl etwas zu laden, der Thread schaut dann in dieser Liste nach und arbeitet sie dann ab.
Wenn dieser einen Auftrag erledigt hat, gibt es dem Hauptprogramm bescheit und macht seine Arbeit weiter. Die Benachrichtigung kannst du über das gennante Observer Muster lösen.
Für ein Anfänger ist es vieleicht wirklich ein bischen viel Stoff aufeinmal, darum würde ich vieleicht die Asynchrone IO von Windows verwenden, da so der ganze Thread Balast entfällt(wird in der API versteckt).
http://support.microsoft.com/kb/156932
http://msdn.microsoft.com/en-us/library/ms683209(VS.85).aspx (leider kommt das board mit dem link nicht klar, darum einfach copy/paste)
Wenn man dann bei GetOverlappedResult den 4. Parameter einfach mit false belegt und das in eine IsReady methode oder so ähnlich verpackt, dann kann man in der gameloop einfach in jeden Durchgang alle noch zu ladenen Blöcke per schleife abfragen ob IsReady true ist und aus der schleife entfernen, sowie alles nötige machen, damit das die Daten verarbeitet werden.

edit:
Mit der Optimierung ist es so eine Sache, man sollte diese wirklich nur verwenden, wenn man diese auch benötigt, also das Zielsystem nicht die entsprechende Leistung erreicht.
Die Zeit, die man in Optimierung steckt, könnte man so in bug tracking stecken, darum bist du da völlig richtig, wenn du dein System bei behälts, wenn es ausreichend ist.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 14 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.020s | 19 Queries | GZIP : On ]