Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

CodingCat

1x Contest-Sieger

  • »CodingCat« ist der Autor dieses Themas

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

1

20.05.2013, 00:48

IDisposable: Diskussion Resource Management

Ich will den ursprünglichen Thread nicht zu sehr aus der Bahn werfen, deshalb sammle und kommentiere ich hier parallel. Ich lade jeden ein, hier mit über den Tellerrand zu schauen. Nachfragen und Mitmachen ausdrücklich erlaubt.

Ressourcenverwaltung in "modernen" Sprachen wie C# und Java

ja, dann muss Player aber IDisposable implementieren, denn sonst kann ich die Methode knicken^^

Willkommen in der wunderbaren Welt der Garbage Collection, wo jegliches Ressourcenmanagement zum Alptraum wird... ;)

Ich wüsste ehrlich gesagt nicht wieso eine Spieler-Klasse disposable sein sollte. Das Teil sollte keine externen unmanaged Ressourcen verwalten.

BlueCobold hat Resource Management (abseits von Memory Management) in Managed-Umgebung auf den Punkt gebracht: Zunächst ist es ist eine Aufgabe, die von Hand erledigt werden muss. In dieser Hinsicht fällt man mit den gängigen "modernen Sprachen" leider weiterhin zurück auf mittelalterliches C-Niveau. Da es aber insbesondere nicht die Aufgabe einer jeden Einzelklasse (-> Spielerklasse) sein kann, bleibt als sinnvolle Lösung letztlich nur die Übertragung dieser Aufgabe an einen verantwortlichen Dritten. Dieser hat dann meist die Gestalt eines "zentralen" (!= globalen) Managers pro Ressourcentyp.

Wobei man vielleicht noch erwähnen sollte: Das sieht ja nach XNA aus und da lädst du deine Texturen ja über den ContentManager. Dieser disposed schon von sich aus alle Objekte, die er geladen hat, wenn er selbst disposed wird.

Ein solcher Manager übernimmt dann die kollektive Verwaltung einer bestimmten Ressourcenart nach bestem Wissen und Gewissen. Wohlgemerkt kann dies ganz analog zu C auch ohne Weiteres in "dangling pointers" oder besser "dangling references" auf freigegebene Ressourcen enden, wenn der Entwickler die Lebensdauer von Ressourcen nicht sorgsam durchdenkt.

Ressourcenverwaltung in C++

C++ ermöglicht über die automatisierte und deterministische Zerstörung von Objekten "auf dem Stack" (automatic storage duration) die Implementierung von automatisierter Referenzzählung und Zerstörung beliebiger Ressourcen. Die aktuelle Standardbibliothek bietet diese Funktionalität analog zu managed Sprachen für die Ressource Speicher fertig implementiert (siehe unique_ptr und shared_ptr); mit etwas C++-Erfahrung lassen sich analoge Klassen für beliebige Ressourcentypen implementieren. Leider ist meine Erfahrung, dass sich die Leute angesichts der großen Vielfalt von speziellen Memberfunktionen (Kopier- und Verschiebungskonstruktor, Zuweisungs- und Verschiebungsoperator) häufig noch schwer tun, solche Klassen auf Anhieb vollständig und korrekt zu implementieren. Prinzipiell bietet C++ im Gegensatz zu oben genannten Sprachen jedoch einen einigermaßen einfachen Mechanismus, Ressourcenverwaltung für beliebige Ressourcentypen lückenlos zu automatisieren.

Ressourcenverwaltung in "modernen" Sprachen 2.0?

Interessant ist nun, dass man nicht zwangsweise den C++-Weg gehen müsste. Wenn oben genannte Sprachen nicht so geschlossen wären, wäre das Anbieten einer umfassenden Schnittstelle zur Erweiterung der Garbage Collection eventuell ebenfalls eine gangbare Lösung. Dann könnten die angesprochenen, ohnehin bereits notwendigen Klassen zur kollektiven Ressourcenverwaltung ("Manager") Gebrauch von GC-Information machen. Insbesondere die Erreichbarkeit von Objekten über Referenzen, die ein GC für Speicherverwaltung ohnehin schon kennt, ließe sich dann mit ressourcenspezifischen Parametern, wie z.B. der aktuellen Verfügbarkeit der verwalteten Ressource, kombinieren, um den entsprechenden Ressourcentyp robust zu verwalten. Auch andere Größen, wie die Zeit seit der letzten Benutzung, wären von Interesse, um analog zu bestehenden Speicher-GCs sinnvolle Eviction-Strategien für andere Ressourcen zu ermöglichen.

Ich kenne im Moment keine Sprache, die diesen Weg geht, und so klafft bei der Entwicklung mit gängigen "modernen" Sprachen eine unbegreifliche Lücke in der Ressourcenverwaltung.

Die Schwierigkeiten einer solchen Schnittstelle liegen auf der Hand: GCs laufen typischerweise asynchron, sie sollten den normalen Programmfluss möglichst wenig beeinträchtigen. Angesichts der programmiertechnischen Schwerigkeiten, die schon bei der Verwaltung einzelner Ressourcen in C++ erkennbar sind, erscheint die Implementierung einer leichtgewichtigen asynchronen Verwaltung eines Ressourcenkollektivs keine Aufgabe, der der durchschnittliche Java-/C#-Programmierer gewachsen sein könnte. Eine interessante Frage wäre nun, ob man die Schnittstelle eines solch erweiterbaren GC-Frameworks so bombensicher hinbekommt, dass der ressourcenspezifische Code nicht mehr allzu viel Spielraum für einen systematischen Fehler abseits der Eigenheiten der jeweils verwalteten Ressourcenart lässt.

Vielleicht hat ja irgendjemand hier irgendwann mal die Gelegenheit, in diese Richtung zu forschen; vielleicht wird sogar bereits Forschung betrieben, Hinweise willkommen.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »CodingCat« (20.05.2013, 00:57)


CodingCat

1x Contest-Sieger

  • »CodingCat« ist der Autor dieses Themas

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

2

20.05.2013, 11:03

Kurzer Nachtrag: Ich habe mich gerade erinnert, dass ich in Java schonmal gezwungenermaßen PhantomReferences nutzen musste, um die Unerreichbarkeit von memory-mapped Files zu prüfen (die in ihrer Standardimplementierung offenbar auch nur halbherzigen Gebrauch vom Speicher-GC Gebrauch machen, sehr unangenehm). Damit ist die Erreichbarkeitsinformation tatsächlich verfügbar, beliebige Referenzen (-> Unmanaged Resources) lassen sich an eine Queue binden, die so lange blockiert, bis die jeweiligen Objekte zum Aufräumen eingereiht werden.

Das Starten eines eigenen Threads zu diesem Zweck bietet sicher noch einiges an Fehlerpotential, aber zumindest der Ansatz ist da. Weitaus problematischer erscheint mir im Moment, dass ich keine konkreten Angaben zum Zeitpunkt des Aufräumens finden kann. Dies legt den Verdacht nahe, dass wir es hier mit ganz normalem Speicher-GC-Verhalten zu tun haben, also Einreihung in die Queue NACH Auswahl durch den Speicher-GC. Solange alle Aufräumarbeiten an Speicherverwaltungsheuristiken gebunden sind, können andere Ressourcen nicht zuverlässig entsprechend eigener Constraints verwaltet werden. Das werdet ihr spätestens dann merken, wenn ihr in Java mal mit großen mem-mapped Files arbeitet und euch permanent der Adressraum überläuft, weil der GC eine Freigabe des Mappings in diesem Moment noch nicht in Erwägung zieht.

Eine weitere Anforderung an ein umfassendes GC-Framework wäre wohl die Aktivierbarkeit von deterministischer Zerstörung für bestimmte "wertvolle Ressourcen". Es ist ja nicht so, dass GCs zwangsweise so undeterministisch laufen müssen, wie sie das momentan praktisch immer tun. Analog zu deterministischer Referenzzählung, wie sie sich z.B. leicht in C++ implementieren lässt, ließe sich auch deterministische GC-Auslösung implementieren; im Grunde dann normale deterministische Referenzzählung erweitert um eine robuste Erreichbarkeitsanalyse, welche das Problem von Referenzzykeln löst.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

3

20.05.2013, 11:08

Für C# könnte ich mir einen Lösungsansatz vorstellen, der eigentlich sehr leicht für Microsoft zu implementieren scheint, aber leider nicht umgesetzt ist: Man könnte bestimmt viel machen, wenn structs einen Finalizer haben könnten. Dieser sollte sofort aufgerufen werden, wenn die Variable von Typ der Struct den Scope verlässt. Leider gibt es das nicht.

Ganz so schlimm wie du sehe ich die Situation mit unmanaged Ressourcen in .NET aber auch nicht. Solange jede Klasse, die unmanaged Ressourcen direkt hält, einen Finalizer implementiert hat man wenigstens kein Memory Leak, im Gegensatz zu einem vergessenen delete in C++. Ganz abgesehen davon, dass man in C++ nicht nur Ressourcen, sondern auch Objekte die nur Speicher benutzen, selber managen muss.

Natürlich, ist das mit dem Finalizer unschön:
- Der GC läuft ggf. viel zu spät, da er nur mitbekommt, wenn managed Speicher allokiert wird.
- Der GC kümmert sich um das Finalizen, was nicht so effizient ist.
- Wenn die Resource etwas wie eine Datei oder Netzwerkverbindung ist, bei der mehrfacher Zugriff evtl. unterbunden wird, handelt man sich evtl. trotzdem Bugs ein.

Solange Punkt 3 nicht zutrifft, betrachte ich disposen daher mehr als Optimierung. Klassen, die IDisposable implementieren, sollten eh auch einen Finalizer implementieren.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

CodingCat

1x Contest-Sieger

  • »CodingCat« ist der Autor dieses Themas

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

4

20.05.2013, 11:44

Für C# könnte ich mir einen Lösungsansatz vorstellen, der eigentlich sehr leicht für Microsoft zu implementieren scheint, aber leider nicht umgesetzt ist: Man könnte bestimmt viel machen, wenn structs einen Finalizer haben könnten. Dieser sollte sofort aufgerufen werden, wenn die Variable von Typ der Struct den Scope verlässt. Leider gibt es das nicht.

Ja, das wäre eine deterministische Lösung analog zu C++.

Ganz so schlimm wie du sehe ich die Situation mit unmanaged Ressourcen in .NET aber auch nicht. Solange jede Klasse, die unmanaged Ressourcen direkt hält, einen Finalizer implementiert hat man wenigstens kein Memory Leak

In C# wird der Aufruf eines Finalizers immerhin zu irgendeinem Zeitpunkt garantiert, in Java hingegen ist der Aufruf von Finalizers optional (dies ist im Grunde konsequenter, weil es anderweitige Ressourcenverwaltung durch den Speicher-GC explizit entwertet; was andere Entwickler nicht davon abhält, es dennoch zu nutzen, inklusive derer der Java-Standard-Bibliothek).

im Gegensatz zu einem vergessenen delete in C++. Ganz abgesehen davon, dass man in C++ nicht nur Ressourcen, sondern auch Objekte die nur Speicher benutzen, selber managen muss.

delete solltest du heute in C++ gar nicht mehr von Hand aufrufen sollen. Stattdessen nutzt du "Referenztypen" wie unique_ptr und shared_ptr, welche die Speicherverwaltung automatisieren. Selbes gilt nach einmaliger Implementierung analoger Referenz-Typen auch für jede andere Ressourcenart.

[3] Wenn die Resource etwas wie eine Datei oder Netzwerkverbindung ist, bei der mehrfacher Zugriff evtl. unterbunden wird, handelt man sich evtl. trotzdem Bugs ein.
Solange Punkt 3 nicht zutrifft, betrachte ich disposen daher mehr als Optimierung. Klassen, die IDisposable implementieren, sollten eh auch einen Finalizer implementieren.

Nicht nur das, sondern der Speicher-GC kann insbesondere nicht auf Engpässe anderer Ressourcen reagieren. Er sieht zwar, wenn der ihm zugedachte Arbeitsspeicher ausgeschöpft ist, und kann entsprechend unerreichbare Objekte freigeben. Er sieht jedoch nicht, wenn VRAM o.ä. Ressourcen ausgeschöpft sind, in diesem Fall fliegt ggf. bei der nächsten Allokation einfach eine OutOfVRAM-Exception, obwohl sich der fehlende Raum angesichts vieler verwaister Referenzen möglicherweise problemlos schaffen ließe. Dass wir auf dieses Problem einigermaßen selten stoßen, liegt in erster Linie an den unfassbar ressourcenreichen Maschinen, mit denen wir heute arbeiten, und daran, dass sich die Entwickler in professionellen Umgebungen nicht zu schade sind, Ressourcenverwaltung weiterhin in stoischer C-Disziplin immer und immer wieder von Hand durchzuführen. (Nicht umsonst gibt es Firmen, die sich auf Consulting bei Leaks in Managed-Sprachen spezialisiert haben.)
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

De_Struktor

unregistriert

5

20.05.2013, 11:52

Hört sich interessant an. Im Bezug auf BC's Aussage, der ja sagt, er würde keine Spielerklasse mit, indem Fall Texture2D, ausstatten.

Dot wiederum behauptet, es ist sehr wohl disposable, was trifft nun denn zu??
Ich habe 4 Klassen, die nach Dot's Aussage, alle IDisposable implementieren müssten, aufgrund von kontrollierter Speicherentsorgung.


Was wäre eine logische und angemessene Implementierung??

De_Struktor

unregistriert

6

20.05.2013, 12:29

Was ich dazu sagen muss, scherzhafterweise, das mit so langsam die Speicherverwaltung komplexer vorkommt als die in C++^^.

Mein Bruder, sagte, er persönlich finde das Ressourcenmanagment in C++ angenehmer, weil er vorher auch mit Java gerarbeitet hatte.

Das kann ich natürlich nicht wissen, aber es ist seine persönliche Meinung.

CodingCat

1x Contest-Sieger

  • »CodingCat« ist der Autor dieses Themas

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

7

20.05.2013, 13:00

Im Bezug auf BC's Aussage, der ja sagt, er würde keine Spielerklasse mit, indem Fall Texture2D, ausstatten. Dot wiederum behauptet, es ist sehr wohl disposable, was trifft nun denn zu?? Ich habe 4 Klassen, die nach Dot's Aussage, alle IDisposable implementieren müssten, aufgrund von kontrollierter Speicherentsorgung.
[...]
Was ich dazu sagen muss, scherzhafterweise, das mit so langsam die Speicherverwaltung komplexer vorkommt als die in C++^^. Mein Bruder, sagte, er persönlich finde das Ressourcenmanagment in C++ angenehmer, weil er vorher auch mit Java gerarbeitet hatte.
[...]
Was wäre eine logische und angemessene Implementierung??

Nunja, dot denkt hier eher dezentral, wie es in C++ einfach, automatisiert und lückenlos umsetzbar wäre. In C# geht das leider nicht vergleichbar automatisiert. Um dort die Benutzung einzelner Ressourcen mit vergleichbarer Präzision zu verfolgen, musst du tatsächlich händisch Dispose()-Funktionen implementieren, die nichts weiter tun, als die jeweils referenzierten Ressourcen freizugeben. Da Ressourcen in einer Spieleumgebung aber zwangsläufig vielfach geteilt werden, dürftest du hier nicht einfach für alle enthaltenen Ressourcen Dispose() aufrufen, sondern müsstest bei geteilten Ressourcen deine eigenen Ressourcenklassen zusätzlich um eigene Referenzzählung erweitern, die dann bei Erreichen eines 0-Zählstandes ihrerseits die Dispose()-Methode für das eigene Objekt auslöst. Fazit: Bei stringenter feingranularer Ressourcenverwaltung kommst du in C# nicht umhin, Dispose()-Methoden transitiv den ganzen Abhängigkeitsbaum mit Ressourcen arbeitender Objekte hinauf in jede Klasse einzufügen.

Die Alternativlösung, die BC anspricht, sind die hier bereits skizzierten Manager-Klassen. Diese übernehmen Ressourcenverwaltung im Kollektiv, ähnlich der Art wie dies der GC mit Speicher tut. In diesem Fall kümmerst du dich andernorts nicht weiter um Ressourcenverwaltung, sondern legst dir beispielsweise für jeden Level, den du lädst, einen neuen Resource-Manager an, über den du dann am Ende des Levels ALLE geladenen Ressourcen auf einen Schlag wieder freigibst. Hier musst du Acht geben, dass du die Ressourcenfreigabe tatsächlich erst dann auslöst, wenn alle verwalteten Ressourcen im weiteren Programmablauf garantiert nicht mehr referenziert werden. In Umgebungen mit einigermaßen geringer Ressourcenfluktuation, wie sie Spielelevels in der Regel darstellen, ist das eine praktikable Lösung. In anderen veränderlicheren Umgebungen, wie z.B. Leveleditoren, ist das weitaus problematischer, weil hier im Extremfall tatsächlich sehr viele große Ressourcen ge- UND entladen werden können.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »CodingCat« (20.05.2013, 13:06)


Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

8

20.05.2013, 16:29

Ganz ehrlich, sicherlich ist das mal interessant und auch spannend über so Zeug wie hier zu sprechen, aber ich behaupte der Alltagsprogrammierer hier wird das meiste davon nicht brauchen. Zumindest nicht in diesen Fällen. Hier geht es doch um Texturen zu Spielen. Ich behaupte wenn de_struktor ein Spiel schreibt wird er sich nicht groß um Ressourcenmanagement für Texturen kümmern müssen. Der Content Manager von XNA kümmert sich für ihn darum und sein Speicher wird ausreichend sein. Natürlich davon ausgegangen dass es sich hier um kein Spiel im High End RPG Style mit 1000en Texturen handelt. Ich selbst habe ja nun einige Jahre mit C# arbeiten dürfen/müssen (eher dürfen) und habe selbst nur sehr selten IDisposable benötigt. C# kann durch den GC auch wirklich ätzend werden, wenn man es dann wirklich benötigt. Meiner Meinung nach aber nicht unbedingt weil der Speicher nicht schnell genug freigegeben wird (ganz ehrlich was schreibt ihr bitte für Anwendungen;) ) sondern eher weil bestimmte Ressourcen nicht schnell genug freigelegt werden. Da wäre zum Beispiel Netzwerk und Sockets ein Beispiel was ja schon angesprochen wurde. Ich denke solange ich Ressourcen nicht selbst manage (unmanaged resources) brauche ich mir in den meisten Fällen bei C# weniger Gedanken um Dispose etc machen. Das schöne an .Net ist doch dass sich um sowas gekümmert wird. Als C++ Entwickler gibt man das nur ungern aus der Hand weil man sich hier halt um alles selbst kümmert, aber bei .Net ist es halt so. Und meiner Meinung nach sind selbst die Smartpointer bei C++ nicht ansatzweise so einfach wie der GC. Das Problem (was ja schon angesprochen wurde) sind ja Referenzzyklen. Und die kann man sich nun mal schnell einhandeln wenn man nicht aufpasst. Dann kann man natürlich am Design schrauben und machen und tun, aber das Design zu ändern nur weil meine Referenzverwaltung sonst nicht klar kommt ist auch nicht der beste Weg. Deswegen denke ich dass das hier alles eine Sache ist bei welcher man stark zwischen Sprachen unterscheiden kann. Wenn man C++ und C# vergleichen möchte kann man auch Assembler und C vergleichen. Beide unterscheiden sich stark voneinander und haben eigentlich auch andere Anwendungszwecke.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

De_Struktor

unregistriert

9

20.05.2013, 16:52

Schorsch, das ist zwar korrekt, nur wenn du vielleicht mein ersten Beitrag gelesen hast, dann wüsstest du, das es mir nur um den Lerneffekt geht, denn eines Tages, werde ich grössere Anwendungen schreiben müssen und dann ist das Thema hier nicht ganz unbedeutend. ich will ja nur lernen, wie man "Ressourcenorientiert" arbeitet. Das ich das in XNA nicht brauche ist klar, aber ich will dennoch wenigstens einigermaßen verstehen, wie ich damit in größeren Geschichten umzugehen weiß!

edit: 1 Beitrag in IDisposable, nicht in dem hier!

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

10

20.05.2013, 17:15

Den hab ich nicht gelesen aber mir ist schon klar worum es hier geht. Du bist ja auch noch nicht lange dabei. Ich wollte nur darauf hinweisen, dass man sich die meisten Probleme gern selbst schafft weil man zu lang überlegt. Ich kenne das von mir selbst und versuche mittlerweile so gut es geht immer weiter davon weg zu kommen. Beispiel ist zum Beispiel Code den man für jede Situation, jeden Fall und alle möglichen Probleme die man nicht hat und nie kriegen wird schreibt. "Früher" habe ich mir teilweise Gedanken gemacht wie man bestimmte Dinge denn noch anders aufrufen könnte/wollte, obwohl das für mich eigentlich uninteressant war. Den Code gibt man im Normalfall dann auch nicht weiter also sollte man eher das machen was grad wichtig ist und weniger, was man sich noch so vorstellen kann. Ausnahmen gibt es natürlich immer. Bei der Arbeit sieht sowas dann ja auch wieder ganz anders aus. Im Prinzip, mach was du musst und mach dir über den Rest erst mal weniger Gedanken. Zweite Sache die ich meine, wenn ich eine Anwendung schreibe, bei der das Ressourcenhandling eine wichtige Rolle spielt, dann benutze ich halt keine .Net Sprache, oder wenigstens nicht für die Parts der Software bei welchen das eine wirklich große Rolle spielt. Ich male auch kein Bild mit einem Bleistift obwohl ich viel mehr auf Aquarelle stehe. Und IDisposable ist wichtig, aber dann bitte für unmanaged Ressourcen.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Werbeanzeige

Ähnliche Themen