Discussion:
Thread/Net Performance
(zu alt für eine Antwort)
Jan Hofmann
2003-10-21 13:30:52 UTC
Permalink
Hallo,

ich habe ein kleines Client/Server System geschrieben. Der Server wartet auf
Clients, akzeptiert diese und erzeugt dann für jeden Client eine
MessageQueue und einen Thread, der wartet ob seine Messagequeue gefüllt wird
und gegebenenfalls die Nachricht an den Client sendet. Der Client wartet in
einer Endlosschleife auf eingehende Nachrichten. Wenn ich das ganze starte,
habe ich bis zum dritten Clienten eine CPU auslastung von 0-2%, aber nach
start des vierten ist sie konstant auf 100%. Ich habe schon ein bisschen mit
sleep(x) rumprobiert, ob ich die CPU last verringern kann, aber es tritt
immer derselbe Effekt auf.
Irgendwelche Ideen, wo mein Problem liegen könnte?
(sockets/threads/hardware)
Bei Bedarf kann ich gerne meinen sourcecode posten, hab dies aber erstmal
nicht getan, da ich nicht sicher bin, ob es nicht vielleicht ein
generelleres Problem ist.

Danke,

Jan Hofmann
Sven Kiesewetter
2003-10-21 16:01:40 UTC
Permalink
Post by Jan Hofmann
ich habe ein kleines Client/Server System geschrieben. Der Server wartet auf
Clients, akzeptiert diese und erzeugt dann für jeden Client eine
MessageQueue und einen Thread, der wartet ob seine Messagequeue gefüllt wird
und gegebenenfalls die Nachricht an den Client sendet. Der Client wartet in
einer Endlosschleife auf eingehende Nachrichten. Wenn ich das ganze starte,
habe ich bis zum dritten Clienten eine CPU auslastung von 0-2%, aber nach
start des vierten ist sie konstant auf 100%. Ich habe schon ein bisschen mit
sleep(x) rumprobiert, ob ich die CPU last verringern kann, aber es tritt
immer derselbe Effekt auf.
Irgendwelche Ideen, wo mein Problem liegen könnte?
(sockets/threads/hardware)
Bei Bedarf kann ich gerne meinen sourcecode posten, hab dies aber erstmal
nicht getan, da ich nicht sicher bin, ob es nicht vielleicht ein
generelleres Problem ist.
Du solltest definitiv auf sinnvolle Synchronisation achten, einfache
synchronized-Blöcke reichen i.a. nicht aus. Wenn du wait und notify
sinnvoll einbaust, kannst du zumindest die Serverlast drastisch senken.
Sleep hilft dir hier kaum weiter, weil es die Objektmonitore nicht
wieder freigibt.
Bei den Clients macht das glaub ich wenig Sinn, weil die Methoden, die
vom NetStream, lesen eh blockierend sind, soweit ich mich erinnere. Bei
Blockade (Stream leer) wird genauso wenige Rechenzeit verbraucht wie bei
sleep oder wait.

Sven
Bernd Eckenfels
2003-10-21 18:13:14 UTC
Permalink
Post by Jan Hofmann
ich habe ein kleines Client/Server System geschrieben. Der Server wartet auf
Clients, akzeptiert diese und erzeugt dann für jeden Client eine
MessageQueue und einen Thread, der wartet ob seine Messagequeue gefüllt wird
und gegebenenfalls die Nachricht an den Client sendet.
Du schreibst hier von Warten, benutzt du da wait()/notify() oder machst du
ein Polling ob Daten da sind.
Post by Jan Hofmann
Der Client wartet in
einer Endlosschleife auf eingehende Nachrichten. Wenn ich das ganze starte,
habe ich bis zum dritten Clienten eine CPU auslastung von 0-2%, aber nach
start des vierten ist sie konstant auf 100%.
Ist das der Server oder der Client der die Zeit zieht? Bist du dir Sicher
dass du keine Exceptions verschluchst? Wieso baust du nicht einfach mal
System.out ein oder debugst das ganze, irgendwas laeuft in einer Schleife,
ganz sicher.

Ich habe schon ein bisschen mit
Post by Jan Hofmann
sleep(x) rumprobiert
Sleep ist _immer_ ein zeichen von Programmierfehlern.

Gibt das schon wieder ein Chat Server?

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Alexander Gran
2003-10-21 22:53:26 UTC
Permalink
Post by Bernd Eckenfels
Sleep ist _immer_ ein zeichen von Programmierfehlern.
Hilf mir mal auf die Sprüng:
Wie programmierst du einen Timeout?
Oder schickst alle 5 Sekunden ein Datenpaket.

Alex
--
Some operating systems are called `user friendly',
Linux however is `expert friendly'.
Encrypted Mails welcome. Send spam to ***@gmx.net, please.
PGP-Key at http://zodiac.dnsalias.org/misc/pgpkey.asc | Key-ID: 0x6D7DD291
Bernd Eckenfels
2003-10-21 23:01:32 UTC
Permalink
Post by Alexander Gran
Wie programmierst du einen Timeout?
Jedenfalls nicht mit Sleep :)

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Alexander Gran
2003-10-22 21:55:51 UTC
Permalink
Post by Bernd Eckenfels
Jedenfalls nicht mit Sleep :)
Das war mir soweit dann doch klar....

Alex
--
Some operating systems are called `user friendly',
Linux however is `expert friendly'.
Encrypted Mails welcome. Send spam to ***@gmx.net, please.
PGP-Key at http://zodiac.dnsalias.org/misc/pgpkey.asc | Key-ID: 0x6D7DD291
Bernd Eckenfels
2003-10-22 23:04:33 UTC
Permalink
Post by Alexander Gran
Post by Bernd Eckenfels
Jedenfalls nicht mit Sleep :)
Das war mir soweit dann doch klar....
Also:

in den wenigen Situationen in denen man wirklich Zeitspannen nichts tun
moechte (das ist natuerlich was anderes als mittels sleep probleme wie
deadlocks, race conditions oder busy loops) würde ich auf jeden Fall wait()
verwenden, und wenn auch auch nur dazu dient den schlafenden thread sauber
aufzuwecken, um das system herunterzufahren.

Ansonsten gibt es auch noch Timer, die man nicht aus den Augen verlieren
sollte, um die Anzahl der parallel nichts tuenden Threads zu reduzieren.

Und dann natuerlich timeouts bei blocking funktionen angeben, statt diese
extern abzubrechen.

-> 3 mal ueberlegen ob man mist gebaut hat, wenn man sleep braucht.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Sascha Broich
2003-10-23 06:39:18 UTC
Permalink
Post by Bernd Eckenfels
Post by Alexander Gran
Post by Bernd Eckenfels
Jedenfalls nicht mit Sleep :)
Das war mir soweit dann doch klar....
in den wenigen Situationen in denen man wirklich Zeitspannen nichts tun
moechte (das ist natuerlich was anderes als mittels sleep probleme wie
deadlocks, race conditions oder busy loops) würde ich auf jeden Fall wait()
verwenden, und wenn auch auch nur dazu dient den schlafenden thread sauber
aufzuwecken, um das system herunterzufahren.
Also ich benutze sleep, um einen Thread schlafenzulegen. Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten. Thread.sleep() hingegen senkt die
Prozessorlast spürbar.

Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(Die unmögliche Tatsache, C. Morgenstern)
--
Bernd Eckenfels
2003-10-23 22:15:55 UTC
Permalink
Post by Sascha Broich
Also ich benutze sleep, um einen Thread schlafenzulegen.
Wozu muss der Thread schlafen?
Post by Sascha Broich
Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten. Thread.sleep() hingegen senkt die
Prozessorlast spürbar.
Was für Interrupts sind das?

Bist du sicher dass nicht irgendwer Notify auf deinem object auf dem du wait
aufrufst verwendest? Bist du sicher dass du den Monitor hast bevor du wait
aufrufst?

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Sascha Broich
2003-10-25 16:41:01 UTC
Permalink
Post by Bernd Eckenfels
Post by Sascha Broich
Also ich benutze sleep, um einen Thread schlafenzulegen.
Wozu muss der Thread schlafen?
Ich habe zum Beispiel Threads für Meßkarten laufen, die mir - ungebremst -
locker die CPU-Last auf 100% bringen, was aber nicht sinnvoll ist, weil die
Anzeige dann gar nicht mehr aktualisiert werden kann. Daher schicke ich
diese Threads mittels sleep für ein paar Millisekunden schlafen, und
schwups - schon funktioniert auch die Anzeige.
Post by Bernd Eckenfels
Post by Sascha Broich
Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten. Thread.sleep() hingegen senkt die
Prozessorlast spürbar.
Was für Interrupts sind das?
IllegalMonitorException, WIMRE.
Post by Bernd Eckenfels
Bist du sicher dass nicht irgendwer Notify auf deinem object auf dem du wait
aufrufst verwendest? Bist du sicher dass du den Monitor hast bevor du wait
aufrufst?
Da habe ich nicht weiter nachgeforscht, denn: Mit sleep läuft alles
wunderbar. Ich sehe auch nicht wirklich den Vorteil von wait gegenüber
sleep.


Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(C. Morgenstern: Eine unmögliche Tatsache)
--
Bernd Eckenfels
2003-10-25 20:02:59 UTC
Permalink
Post by Sascha Broich
IllegalMonitorException, WIMRE.
Post by Bernd Eckenfels
Bist du sicher dass nicht irgendwer Notify auf deinem object auf dem du wait
aufrufst verwendest? Bist du sicher dass du den Monitor hast bevor du wait
aufrufst?
Da habe ich nicht weiter nachgeforscht
IllegalMonitorException heisst genau das. Dein wait waitet nicht sondern
beschwert sich, dass du vergessen hast auf dem object zu synchronisieren.
Post by Sascha Broich
wunderbar. Ich sehe auch nicht wirklich den Vorteil von wait gegenüber
sleep.
in deinem fall ist das ok.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Paul Ebermann
2003-10-23 22:58:16 UTC
Permalink
Post by Sascha Broich
Post by Bernd Eckenfels
in den wenigen Situationen in denen man wirklich Zeitspannen nichts tun
moechte (das ist natuerlich was anderes als mittels sleep probleme wie
deadlocks, race conditions oder busy loops) würde ich auf jeden Fall wait()
verwenden, und wenn auch auch nur dazu dient den schlafenden thread sauber
aufzuwecken, um das system herunterzufahren.
Also ich benutze sleep, um einen Thread schlafenzulegen. Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten.
Wer sagt dir denn, dass du .interrupt() benutzen
sollst?

Zum Aufwecken gibt es .notify().

Paul
Sascha Broich
2003-10-25 17:37:26 UTC
Permalink
Post by Paul Ebermann
Post by Sascha Broich
Also ich benutze sleep, um einen Thread schlafenzulegen. Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten.
Wer sagt dir denn, dass du .interrupt() benutzen
sollst?
Ich meine Unterbrechungen durch IllegalMonitorExceptions, die das try-catch
für das wait aktivieren.
Wenn ich meinen Thread bewußt aufwecken will, benutze ich natürlich
interrupt. Aber dann ist das auch so gewollt.
Post by Paul Ebermann
Zum Aufwecken gibt es .notify().
Da sollte ja gar nichts aufgeweckt werden. Die Exceptions wurden wohl durch
notifyAll oder so von woanders herangetragen.
Das wait/notify-Konzept habe ich bisher noch nicht benutzt. Sleep/interrupt
war für mich bisher sinnvoller.


Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(C. Morgenstern: Eine unmögliche Tatsache)
--
Paul Ebermann
2003-10-26 21:05:02 UTC
Permalink
Post by Sascha Broich
Post by Paul Ebermann
Post by Sascha Broich
Also ich benutze sleep, um einen Thread schlafenzulegen. Mit wait gibt es
bei mehreren Threads so viele Interrupte, daß der Prozessor ausgelastet
ist, das try-catch abzuarbeiten.
Wer sagt dir denn, dass du .interrupt() benutzen
sollst?
Ich meine Unterbrechungen durch IllegalMonitorExceptions, die das try-catch
für das wait aktivieren.
Eine IllegalMonitorStateException gibt es dann,
wenn du wait aufrufst, ohne in einer synchronized-Methode
oder einem synchronized-Block des entsprechenden Objektes
zu sein.

Die korrekte Anwendung ist also:

synchronized(objekt)
{
// evtl. noch etwas, was diesen Monitor braucht
objekt.wait(dauer);
}


Falls du den Thread dann von außen aufwecken willst,
nimmst du objekt.notify() bzw. objekt.notifyAll()
(jeweils wieder in einem synchronized(objekt)-Block),
und dann gibt es auch keine Exception (in keinem der
beiden Threads).


Paul
Sascha Broich
2003-10-27 08:21:40 UTC
Permalink
Post by Paul Ebermann
synchronized(objekt)
{
// evtl. noch etwas, was diesen Monitor braucht
objekt.wait(dauer);
}
Falls du den Thread dann von außen aufwecken willst,
nimmst du objekt.notify() bzw. objekt.notifyAll()
(jeweils wieder in einem synchronized(objekt)-Block),
und dann gibt es auch keine Exception (in keinem der
beiden Threads).
Ah, ja. Dann habe ich aber den synchronized-Overhead.
Ich denke, daß sleep zum Thread-schlafen-Legen anscheinend doch die bessere
Wahl ist.


Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(Die unmögliche Tatsache, C. Morgenstern)
--
Hendrik Lipka
2003-10-27 11:03:49 UTC
Permalink
On Mon, 27 Oct 2003 09:21:40 +0100, Sascha Broich
Post by Sascha Broich
Ah, ja. Dann habe ich aber den synchronized-Overhead.
Urban Legend. Ein halbwegs aktuelles JDK hat da keinen Overhead mehr.
Post by Sascha Broich
Ich denke, daß sleep zum Thread-schlafen-Legen anscheinend doch die bessere
Wahl ist.
Solange Deine Threads zum Zeitpunkt des sleep() _garantiert_ keine Monitore
halten (noch nicht mal durch den Aufruf einer synchronized-Methode), aknnst Du
das machen. Ansonsten wirst Du früher oder später in Problem laufen - weil
sleep() eben keinen Monitore freigibt, wait() aber sehr wohl.

sleep() sollte man nicht verwenden. Die Kombination wait-notify ist um Längen
performanter, weil Threads nur dann aufwachen, wenn sie wirklich was zu tun
haben. sleep() ist nicht weiter als busy-polling, welches ein klein wenig
besser skaliert.
Und wenn man nur eine gewisse Zeit lang warten will, kann das auch wait().

Lies mal "Thinking in Java" von Bruce Eckel, der hat das ziemlich gut erklärt
(http://mindview.net/Books/TIJ/index_html).

hli
--
Gewalt ist die letzte Zuflucht des Unfaehigen
Bernd Eckenfels
2003-10-27 21:40:15 UTC
Permalink
Post by Hendrik Lipka
haben. sleep() ist nicht weiter als busy-polling, welches ein klein wenig
besser skaliert.
aeh das ist nicht richtig ausgedrueckt. Sleep macht kein busy polling, und
wenn man nur kurze zeiten warten will, dann muss man auch keine schleife
machen um flags zu pruefen. Aber: man sollte sich genau ueberlegen warum man
sleeped. Datenerfassung oder multimedia syncs sind so ziemlich die einzigen
kurz-pausen die ich mir da denken kann.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Paul Ebermann
2003-10-27 22:25:03 UTC
Permalink
Post by Hendrik Lipka
On Mon, 27 Oct 2003 09:21:40 +0100, Sascha Broich
Post by Sascha Broich
Ich denke, daß sleep zum Thread-schlafen-Legen anscheinend doch die bessere
Wahl ist.
Solange Deine Threads zum Zeitpunkt des sleep() _garantiert_ keine Monitore
halten (noch nicht mal durch den Aufruf einer synchronized-Methode), aknnst Du
das machen. Ansonsten wirst Du früher oder später in Problem laufen - weil
sleep() eben keinen Monitore freigibt, wait() aber sehr wohl.
wait() gibt aber nur den Monitor des Objektes frei,
auf dem es gerade aufgerufen wird, nicht alle Monitore,
die der Thread gerade hält.

Und auch hier sollte man aufpassen, ob das
Monitor-Freigeben überhaupt erwünscht ist - man
hält einen Monitor ja nicht so zum Spaß, sondern
wegen irgendeinem Zweck (z.B. Objekt vor inkonsistenten
Zustand schützen).

Thread.sleep könnte also in etwa so implementiert
werden (throws ergänzen):

public static void sleep(int zeit)
{
Object temp = new Object();
synchronized(temp)
{
temp.wait(zeit);
}
}


Paul

Bernd Eckenfels
2003-10-27 21:37:45 UTC
Permalink
Post by Sascha Broich
Ich denke, daß sleep zum Thread-schlafen-Legen anscheinend doch die bessere
Wahl ist.
sleep ist auch synchronized.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Alexander Gran
2003-10-24 08:06:21 UTC
Permalink
Post by Bernd Eckenfels
in den wenigen Situationen in denen man wirklich Zeitspannen nichts tun
moechte (das ist natuerlich was anderes als mittels sleep probleme wie
deadlocks, race conditions oder busy loops) würde ich auf jeden Fall
ACK. Wobei busy waiting bei manch hirntoter API nicht zu umgehen ist.
Post by Bernd Eckenfels
wait() verwenden, und wenn auch auch nur dazu dient den schlafenden thread
sauber aufzuwecken, um das system herunterzufahren.
Dafür nehm ich eingentlich shutdownhooks.
Post by Bernd Eckenfels
Und dann natuerlich timeouts bei blocking funktionen angeben, statt diese
extern abzubrechen.
Wenn das denn geht..
Weiß eigentlich jmd, wie Thread.sleep/Object.wait implementiert sind?
Post by Bernd Eckenfels
-> 3 mal ueberlegen ob man mist gebaut hat, wenn man sleep braucht.
ACK.
--
Some operating systems are called `user friendly',
Linux however is `expert friendly'.
Encrypted Mails welcome. Send spam to ***@gmx.net, please.
PGP-Key at http://zodiac.dnsalias.org/misc/pgpkey.asc | Key-ID: 0x6D7DD291
Bernd Eckenfels
2003-10-24 08:36:40 UTC
Permalink
Post by Alexander Gran
Weiß eigentlich jmd, wie Thread.sleep/Object.wait implementiert sind?
Thread.sleep() ist nativ, das weiss ich zufaellig weil ich es gestern
nachgesehen habe. Ich hatte vermutet es ist mittels wait() implementiert.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Sascha Broich
2003-10-25 16:51:19 UTC
Permalink
Post by Bernd Eckenfels
Post by Alexander Gran
Weiß eigentlich jmd, wie Thread.sleep/Object.wait implementiert sind?
Thread.sleep() ist nativ, das weiss ich zufaellig weil ich es gestern
nachgesehen habe. Ich hatte vermutet es ist mittels wait() implementiert.
Ich vermute hingegen, daß es die im OS vorhandene sleep-Funktion verwendet.


Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(C. Morgenstern: Eine unmögliche Tatsache)
--
Bernd Eckenfels
2003-10-24 08:38:57 UTC
Permalink
Post by Alexander Gran
ACK. Wobei busy waiting bei manch hirntoter API nicht zu umgehen ist.
Da fallen mir Dinge wie z.b. das pollen von Verzeichnissen auf Änderungen
ein. Java APIs selbst fallen mir jetzt keine ein. Dir?
Post by Alexander Gran
Post by Bernd Eckenfels
wait() verwenden, und wenn auch auch nur dazu dient den schlafenden thread
sauber aufzuwecken, um das system herunterzufahren.
Dafür nehm ich eingentlich shutdownhooks.
Kommt auf die Anwendung an, dine Stand-Alone Anwendung, da muss man sich
keinen Kopf zerbrechen, aber wenn ich z.b. ein servlet hat das im
Hintergrund Threads startet, dann will ich sicher sein die Threads in der
destroy() sauber los zu werden. Hier mache ich dann ein notifyAll() gefolgt
von einer Pause (ja hier hab ich nix gegen sleep) und einem interrup().

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Alexander Gran
2003-10-27 12:28:32 UTC
Permalink
Post by Bernd Eckenfels
Da fallen mir Dinge wie z.b. das pollen von Verzeichnissen auf Änderungen
ein. Java APIs selbst fallen mir jetzt keine ein. Dir?
Nein, jdk api's (gerade, ich kenn nicht alle ;) )nicht. Aber eingentlich
immer wenn man mit der Umwelt spricht, die nicht java ist(d.H. man kann
nichts listener-ähnliches registrieren). Dazu zählen auch (web)-server etc.
Oder als Beispiel ein heartbeat in einem Licenseserver.

Grüße
Alex
--
Some operating systems are called `user friendly',
Linux however is `expert friendly'.
Encrypted Mails welcome. Send spam to ***@gmx.net, please.
PGP-Key at http://zodiac.dnsalias.org/misc/pgpkey.asc | Key-ID: 0x6D7DD291
Hendrik Lipka
2003-10-27 13:53:35 UTC
Permalink
On Fri, 24 Oct 2003 08:38:57 +0000 (UTC), Bernd Eckenfels
Post by Bernd Eckenfels
keinen Kopf zerbrechen, aber wenn ich z.b. ein servlet hat das im
Hintergrund Threads startet, dann will ich sicher sein die Threads in der
destroy() sauber los zu werden. Hier mache ich dann ein notifyAll() gefolgt
Was spricht gegen ein Thread.join()? Die pause garantiert Dir ja nicht, dass
die Threads wirklich alle fertig sind. Könnte ja sein, dass einer derselben
gerade 1MB an ein Modem ausliefern muss...
Post by Bernd Eckenfels
von einer Pause (ja hier hab ich nix gegen sleep) und einem interrup().
Wie gesagt: sleep() sollte man nicht verwenden, sondern wait().

hli
--
Gewalt ist die letzte Zuflucht des Unfaehigen
Bernd Eckenfels
2003-10-27 21:46:32 UTC
Permalink
Post by Hendrik Lipka
Was spricht gegen ein Thread.join()? Die pause garantiert Dir ja nicht, dass
die Threads wirklich alle fertig sind. Könnte ja sein, dass einer derselben
gerade 1MB an ein Modem ausliefern muss...
Ich will vermeiden dass mir der Web Container den destroy() thread killt.
Deswegen moechte ich ein upper bound haben.
Post by Hendrik Lipka
Wie gesagt: sleep() sollte man nicht verwenden, sondern wait().
In dem Fall ist es egal, da die methode synchronized ist, und auch sehr
final. Im Gegenteil, hier ist es sogar gut dass ich mir keine Monitore holen
muss, das vermindert deadlocks.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Lesen Sie weiter auf narkive:
Loading...