Ressourcenleck durch (virtuellen/echten) Thread

Barista

Top Contributor
Bei meiner Beschäftigung mit meinem Generator ist mir eingefallen, dass ein Ressourcen-Leck entstehen könnte, wenn der Consumer-Thread keine Daten mehr aus der Queue liest und der Supplier-Thread darauf wartet, seine Daten loszuwerden.

Zwar wird die Referenz aus dem Consumer-Thread irgendwann vernichtet, aber der (virtuelle oder echte) Thread hat eine Referenz auf die Queue und ist selbst irgendwo noch referenziert, solange die run-Methode nicht beendet wurde.

Das könnte auch in einer Web-App passieren, wenn der Benutzer den Browser schließt ohne den aktuellen Geschäftsprozess zu beenden.

Vielleicht könnte man einen Timeout machen, um das Leck zu beseitigen?

Kennt jemand noch eine andere/bessere Lösung?
 
Ein Thread existiert mindestens so lange bis er mit seiner Arbeit fertig ist. Also auch wenn er wartet. Man kann ihn auch nicht von außen beenden und auch nicht neu starten. Um einen Thread zu beenden ruft man .interrupt() auf und der Thread/Runnable/Task/Future muss intern den Interruptet-State auswerten und seine Arbeit einstellen, bspw. eine Schleife verlassen. Falls es noch Referenzen gibt, werden die Ressourcen vom GC auch nicht entsorgt, wie bei anderen Objekten auch.

Am einfachsten kann man damit umgehen, wenn man die Threads nicht selbst verwaltet. Über ExecutorService kann man so etwas sehr gut steuern. Dabei gibt es auch die Möglichkeit von Timeouts.

Eine Web-Anwendung ist aber noch mal eine andere Kategorie. Im JEE/JakartaEE-Umfeld sollte man keine Threads selbst verwalten. Es gibt da entsprechende Mechanismen der Plattform. Eigene Threads könnten das Transaktions-, Speicher- und Classloading-Managment stören.

Bei Spring bietet sich @Async und CompletableFuture an.
 
Ich würde Dir Empfehlen, hier die Problematiken deutlich besser aufzuteilen. Du hast hier Themen massiv vermischt, die nicht zu vermischen sind alleine schon, weil diese nicht zu vermischen sind (Weil auf komplett unterschiedlichen Ebenen!). Nehmen wir das Beispiel der Webanwendung:

Du hast da erst einmal die Hi Level Ebene:
  • Der Client sendet einen Request
  • Der Server macht daraufhin etwas (ohne Details zu betrachten) und schickt dann die Antwort - Request erledigt.

Da gibt es bei dieser Sicht de genannte Problematik einfach nicht, dass der Server da irgendwas macht um dann zu warten ob da evtl. noch weitere Requests kommen. Klar, mit einer Stateful Connection / Session kann man sich da irgendwas bauen, aber das führt dann tatsächlich dazu, dass man dann in diverse Probleme hinein rennt wie z.B. eine direkte Limitierung der Anzahl der möglichen parallelen Clients und so.

Daher bedeutet das doch unter dem Strich, dass Du eine Art Paging hast. Es kommt der Request. Ja, das Ergebnis könnte von mir aus von 0 ... MAX_LONG gehen, aber pro Request kommen genau x Elemente (also z.B. 100 oder 1000). Wenn also der erste Request kommt, dann baust Du den "Generator" von 0...999 ... beim folgenden Request kommt dann 1000 ... 1999, ... Aber natürlich bist Du fertig, wenn Du das Ergebnis da hast.

Das ist ja auch, was Du jetzt schon hast: Du generierst den Generator (allerdings mit Ziel MAX_LONG) um dann nach 999 Elementen zu stoppen.

Klar, es kann jetzt Folgeprobleme geben. Die Erzeugung des Generators kann extrem teuer sein und der Client mag sehr viele Requests abschicken, die bedient werden müssten. Dann kann es Sinn machen, nicht für jeden Request einen neuen Generator zu erzeugen. Das ist aber dann doch keine wirkliche Thematik vom Generator. Der Generator generiert nur. Und das innerhalb gewisser Grenzen. Hier kommen dann einfach die für diese Probleme typischen Lösungen ins Spiel - die alle auf unterschiedlichen Ebenen ansetzen und unabhängig voneinander sind:
a) Caching Möglichkeiten. Dann hast Du halt ggf. bei einem Request doch einen etwas universelleren Generator gebaut. Dieser landet in einem Cache. Anfragen gehen also immer an den Cache um einen Generator zu bekommen. Und der Cache kann die Verwaltung aktiv übernehmen. Ein typisches Beispiel hier wäre z.B. das Connection Pooling.
b) Evtl. hast Du tatsächlich eine Art Session. Welcher Art das ist, ist offen. Wichtig ist dann, dass jeder CLient einen aktiven Gegenpart hat. Und dieser Gegenpart wird verwaltet. Bei der typischen Session wäre das dann tatsächlich der mögliche Timeout, der die Session dann nach gewisser Zeit beendet. Hier hat man aber meist einfach nur, dass Daten vorgehalten werden. Alleine schon, um skalierbar zu bleiben ... Aber das wäre ein (altes) Thema, zu dem man sehr viel findet inkl Dingen wie eine zentrale Verwaltung von Sessions. Eine aktuelle Variante könnte hier sein, dass man sowas über WebSocket anspricht. Dein Client baut also eine Verbindung auf und die kann dann etwas machen. Die Verbindung wird dann irgendwann geschlossen und das umfasst auch den Fall: Webanwendung wird geschlossen.

Oft hat man sowas aber auch komplett getrennt: Irgendwas wird generiert, weil es generiert werden muss. Das läuft dann unabhängig von Clients und so. Einfaches Beispiel: Jahres- oder Monatsabschluss. Das läuft dann im Hintergrund und es wird irgendwas an Daten generiert und gespeichert. Das mag zwar von einem Client angetriggert worden sein, aber das ist dann ein eigenständiger Ablauf. Clients können dann die Ergebnisse abrufen und betrachten.

Was für Ansätze möglich und denkbar sind, hängt von den genauen Anforderungen ab und was da genau gemacht wird. Aber das hat auf den Generator relativ wenig Einfluss. Der Generator mag da dann ggf. unterschiedliche Features benötigen, aber dies sind dann immer Low Level Features - halt auf Ebene des Generators.

Zwar wird die Referenz aus dem Consumer-Thread irgendwann vernichtet, aber der (virtuelle oder echte) Thread hat eine Referenz auf die Queue und ist selbst irgendwo noch referenziert, solange die run-Methode nicht beendet wurde.
Auf diesen konkreten Fall möchte ich noch eingehen: Das ist doch erst einmal kein wirkliches Problem aber es erfordert natürlich ein sauberes Programmieren. In Java wäre es das try-with-resources und AutoClosable. In C# wäre es IDisposable. Dein Consumer Thread ist verantwortlich für den Generator. Und wenn der Consumer Thread fertig ist, dann wird halt der Generator geschlossen. Und der Generator ist natürlich so zu schreiben, dass hier ein Schließen möglich ist. Also wenn Du hier mit eigenen (virtuellen) Threads arbeitest, dann hast diese zu wecken und der Thread muss sich dann auch beenden. (So Du hier per einzelnen Threads vorgehen willst. Denkbar sind auch andere Ansätze aber die kommen halt vor allem aus der Zeit vor virtuellen Threads.)

Also ja: Das sind durchaus typische Dinge und man muss hier tatsächlich sauber entwickeln. Und wenn man dann Java Mittel anschaut, dann kann der Generator sowas wie ein Stream sein - und wenn wir uns Stream<T> ansehen: dieser implementiert AutoClosable...
 
In Java wäre es das try-with-resources und AutoClosable.
Das hilft nicht, wenn der virtuelle Thread über mehrere Requests leben soll.

Das ist doch erst einmal kein wirkliches Problem
Ich habe damit kein Problem, ist mir aber eingefallen. Ich hätte das Thema lieber unter der Kategorie Diskussion erstellen sollen.
Daher bedeutet das doch unter dem Strich, dass Du eine Art Paging hast. Es kommt der Request. Ja, das Ergebnis könnte von mir aus von 0 ... MAX_LONG gehen, aber pro Request kommen genau x Elemente (also z.B. 100 oder 1000). Wenn also der erste Request kommt, dann baust Du den "Generator" von 0...999 ... beim folgenden Request kommt dann 1000 ... 1999, ... Aber natürlich bist Du fertig, wenn Du das Ergebnis da hast.
Paging wäre ein Beispiel.

Der Generator wird nicht geschlossen, wenn der Client nicht alle Seiten abholt.
Also wenn Du hier mit eigenen (virtuellen) Threads arbeitest, dann hast diese zu wecken und der Thread muss sich dann auch beenden.
Ich habe zu wenig Ahnung davon, Die bisherigen Beispiele mit virtuellen Threads, die ich gesehen habe, haben mit eigenen Pools gearbeitet.
Java:
Thread.ofVirtual().start(runnable);

Keine Ahnung, wie man einen virtuellen Thread mit einem konkreten echten Thread aufweckt.

In den vielen Frameworks, die virtuelle Threads einsetzen, wird es wahrscheinlich eine Lösung geben.
 
Keine Ahnung, wie man einen virtuellen Thread mit einem konkreten echten Thread aufweckt.
Dann schau doch einfach einmal in die Dokumentation! Dann erkennst Du, dass Du bei start weiterhin eine Thread-Instanz zurück bekommst. Hier wird also nicht mehr unterschieden zwischen virtuellen Threads und eben den nicht virtuellen Threads.

Dazu brauchst Du auch keine zusätzlichen Frameworks und so.

Der Generator wird nicht geschlossen, wenn der Client nicht alle Seiten abholt.
Die bisherigen Beispiele mit virtuellen Threads, die ich gesehen habe, haben mit eigenen Pools gearbeitet.
Also hier ist die generelle Fragestellung doch: Wozu braucht der Generator einen eigenen Thread? Was willst / musst Du erreichen? Was sind die genauen Anforderungen?

Der generelle Ansatz wäre aus meiner Sicht, da einfach Schritt für Schritt heran zu gehen. Um dann immer mehr an Anforderungen hinzu zu nehmen und umzusetzen. Und dann nicht den Ansatz wählen: Andere Sprachen / Frameworks haben xyz - wie kann ich xyz in Java 1:1 umsetzen. Statt dessen solltest Du schauen: Was für Mittel gibt mir das Umfeld, welches Du nutzen willst, Dir an die Hand. Das sind dann z.B. die Mittel von Java. Aber wenn Du spezielle Frameworks / APIs nutzen willst wie Spring Boot, Jakarta EE oder ähnliches, dann geht es auch vor allem um diese. (Dazu dann ja auch der Post von @Oneixee5, der da auf diverse Punkte hingewiesen hat.

Der Sinn eines solchen Generators wäre das Überleben mehrerer Requests, das Erhalten des Status über mehrere Requests.
Diesbezüglich habe ich Dir ja auch einige Hinweise gegeben, was es diesbezüglich für Möglichkeiten / Ideen gibt.

Es ging nicht um das vermischen von Themen, sondern um das Zeigen von Möglichkeiten, unter denen solche Lecks entstehen können.
Ich sehe das etwas anders. Denn ich sehe da zum einen die Thematik, dass da Ressourcen nicht richtig verwaltet werden (Der Hinweis mit try-with-resources und AutoClosable) und zum Anderen, dass dann das Kein Thema mehr des Generators selbst ist (Der würde bei Close halt dafür sorgen, dass ein möglicher Thread beendet wurde - so dass eben die notwendige Implementierung sein sollte.) sondern eben die Nutzung des Generators. Diesbezüglich habe ich ja Hinweise gegeben Richtung Sessions, Caches/Pools, ...
Aber wie gesagt: Das sind zwei getrennte Themen bei der Implementierung: Das eine ist der Generator, den Du entwickelst und davon losgetrennt ist dann die Verwendung des Generators in irgendwelchen Umgebungen.

Es gibt eine Methode zum Parken.

Die Methode zum Unparken hat aber keinen Thread als Parameter, setzt eher Infos für den Pool oder so.

Die alten suspend- und resume-Methoden sind deprecated.
Thread.sleep() und Thread.interrupt() ist vermutlich, was Du Dir näher ansehen möchtest.
 
Wozu braucht der Generator einen eigenen Thread? Was willst / musst Du erreichen? Was sind die genauen Anforderungen?
Das habe ich ganz am Anfang in dem anderen Gespräch beschrieben.
Aber wenn Du spezielle Frameworks / APIs nutzen willst wie Spring Boot, Jakarta EE oder ähnliches, dann geht es auch vor allem um diese. (Dazu dann ja auch der Post von @Oneixee5, der da auf diverse Punkte hingewiesen hat.
Will ich nicht nutzen, danke für unerwünschte Hinweise.

Mir wird das hier zu polemisch.
Aber besser als gar keine Reaktion.

Es gibt hier leider keinen passenden Gesprächspartner für mich zu diesem Thema in diesem Forum.
 
Das habe ich ganz am Anfang in dem anderen Gespräch beschrieben.
Nein, hast Du aus meiner Sicht nicht.

Mir wird das hier zu polemisch.
Was genau war bitte polemisch?

Es gibt hier leider keinen passenden Gesprächspartner für mich zu diesem Thema in diesem Forum.
Die Antworten hängen nun einmal auch immer mit davon ab, wie viel Mühe man sich gibt, bei seinen Anfragen....

Wenn ich vermuten müsste, dann würde ich behaupten, dass Du eingeschnappt bist, weil ich nicht Deiner Meinung war in dem anderen Thread bezüglich Klassen und Instanzen. Du suchst Unterstützung hier und lehnst jegliche sachliche Kommunikation geradezu ab. Und nein - hier im Forum sind keine Gedankenleser oder ähnliches. Sorry, wenn Du davon so massiv enttäuscht bist. Aber evtl. findest Du ja irgendwo ein Forum in dem nicht nur Muggle sind. (Ja, das war jetzt tatsächlich polemisch!)
 

Zurück
Oben