Multithreading: Wie viele Threads sollte ich erstellen?

Hallo!

List<Klasse> liste

sei gegeben. Für jedes Objekt von Klasse in der Liste soll nun eine bestimmte Aufgabe ausgeführt werden.
Zum Beispiel Email senden, API Aufruf an einen Server, Web-Scraping nach einer URL usw.

Diese Prozesse dauern bei mir meist 30-40min, da die Liste meist lang ist und die Aufgabe auch etwas Zeit kostet.

Meine Idee um das ganze zu beschleunigen:
Ich erstelle mehrere Threads, die alle auf liste zugreifen (threadsicher dann, vlt lieber BlockingQueQue). Wenn eine Aufgabe fertig ist, wird das Objekt aus der Liste entfernt, sodass jede Aufgabe 1x erledigt wird.

Doch wie viele Threads wären hier sinnvoll? Ich habe 4 Kerne.
Wenn ich richtig informiert bin, wären also bis zu 8 Threads gleichzeitig möglich, oder?

Ich würde vermuten, es wäre am besten, 8 Threads zu nutzen, da die Gesamtprozesse wie gesagt meist 30-40min dauern. Der Zusatzaufwand durch Datenaustäusche zwischen Threads, sowie das erstellen der Threads etc. würde doch wohl kaum das durch acht teilen von 40 zunichte machen.

Was meint ihr?
Grüße
 
Es hängt von der "arithmetic density" (also dem Verhältnis der Zeit, die die CPU tatsächlich Instruktionen ausführt, zur Zeit, die CPU bzw. der Thread mit Warten auf I/O verbringt) deiner Aufgaben ab, wieviele Threads sinnvoll sind. Wenn du hauptsächlich auf I/O wartest, was bei E-Mail Senden, WebService-Aufrufe und Web-Scraping wohl der Fall ist, dann reicht 1 Thread und non-blocking I/O.
 
Ich fürchte ich missverstehe das Warten auf i/o.
Beispiel der E-Mail:
Angenommen ich habe eine Liste von 20.000 Emails und möchte stets denselben Text versenden. Worauf muss da gewartet werden?

Nach dem eine Mail gesendet ist, würde ich diese Mail einfach aus der Liste entfernen lassen vom Thread und so lange warten, bis die Liste leer.

Wieso wäre es langsamer mehrere Threads zu nutzen?


Vielen Dank für die Antwort!
 
Worauf muss da gewartet werden?
1. SSL/TLS Handshake zwischen Client und Server
2. TCP Acknowledges für jedes versendete Paket
3. Warten, dass der Email/SMTP-Server die E-Mail verarbeitet hat
4. Verbindungsabbau auf beiden Seiten

99.9999% der Zeit, die du "eine E-Mail versendest", wird auf I/O bzw. ein externes System gewartet.

Schau dir z.B. mal diese Library hier an: https://github.com/HubSpot/NioSmtpClient

Da du aber wahrscheinlich noch nie etwas mit Non-Blocking I/O gemacht hast, würde ich in deinem Fall tatsächlich einfach mehrere Threads verwenden. Da die Threads die meiste Zeit nur im Warte-Zustand sein werden, müsste deine Anwendung auch ziemlich linear mit der Anzahl der Threads skalieren.

Wieso wäre es langsamer mehrere Threads zu nutzen?
Ich habe nicht gesagt, dass es mit mehreren Threads langsamer wäre. Ich sagte, dass ein Thread ausreicht, wenn du Non-Blocking I/O nutzen würdest.
 
K

kneitzel

Wieso wäre es langsamer mehrere Threads zu nutzen?
Der Nachteil bei mehreren Threads sind die sogenannten Context Switche, die notwendig sind. Daher ist es meist nicht ratsam, mit sehr vielen Threads zu arbeiten, da hier die Anzahl der Context Switche sehr hoch wird.

Also bei IO lastigen Aufgaben kann es dann also sehr gut sein, dass man eben keine blocking calls verwendet. Java hat diesbezüglich z.B. in java.nio die Channels geliefert.

Aber da man heute immer mehrere Cores hat (Laptops kann man teilweise vernachlässigen weil es da teilweise nur 2 Cores gibt in Low End Bereich, bei Servern gibt es schon enorme "Monster" a.la. 2 CPUs mit jeweils > 20 physischen Cores und so ...), macht es durchaus Sinn, Arbeit auch etwas auf mehrere Threads aufzuteilen, das ggf. auch dynamisch. Aber das hängt auch immer stark davon ab, ob etwas parallelisierbar ist.
Beispiel wäre z.B.: Wenn du parallel Emails abliefern willst, muss das der Email-Server auch annehmen. Hier gibt es oft entsprechende Beschränkungen, die eine Parallelisierung verhindern oder zumindest einschränken. Also auch wenn es technisch auf Clientebene möglich wäre, mit mehreren Threads per no blocking calls parallel hundert Emails auf einmal an den Server zu geben, wird dies kein Mailserver mitmachen :)

So viel einfach einmal von meiner Seite.
 
Im Zweifel einfach testen / Monitoren.
D.h. Programm laufen lassen und immer die Threads erhöhen und die Zeit messen.
 
Danke für alle Antworten! Ich werde es denke ich demnächst mal testen.

Sollte ich einen Threadpool nutzen oder die Threads einzeln erstellen?

Grüße
 
K

kneitzel

Wie du Tasks erstellen willst und was du da genau wie verwenden willst, hängt von der Anwendung ab.

ThreadPool macht Sinn, wenn du ständig neue Threads erstellen willst/musst, denn da wird ein Thread sozusagen 'wiederverwendet'. Man spart sich also ggf. etwas Overhead durch ständiges Erstellen und initialisieren von Threads. (Threads sind generell nicht wieder zu verwenden. Das gilt auch für einen Thread Pool. Daher ist das intern etwas anders gelöst, so dass ein Thread nach Beendigung der Aufgabe sich eben nicht beendet sondern wieder als bereitstehender Thread in eine Schlange stellt und sich dann 'blockiert' bis er gebraucht wird. Aber das sind interne Feinheiten....)
 
Übrigens, wen es interessiert: Habe es bei Web-Scraping getestet. 3 Threads waren ca. doppelt so schnell wie 1 Thread. Die Aufgabe insgesamt dauerte vlt eine Minute bei dem schnelleren.

Email und so weiter werde ich auch noch testen.

Grüße
 
3 Threads waren ca. doppelt so schnell wie 1 Thread. Die Aufgabe insgesamt dauerte vlt eine Minute bei dem schnelleren.
Klar, Du nutzt die Latenzzeiten besser aus und kompensierst ggf. zu langsame Übertragungsraten.

Stell Dir mal eine Situation vor, in der es keine Latenzzeiten gibt und die Übertragung auf max. Geschwindigkeit läuft. Dann können weitere Threads (höchstens) nichts bringen, denn schneller als "maximal schnell" geht nicht.

Wenn Du öfter Dateien parallel im Browser heruntergeladen hast, müsstest Du den Effekt kennen: wenn eine Quelle lahm ist, kannst Du einen zweiten Download starten. Wenn die zweite Quelle ebenfalls nicht die schnellste ist, dann erhöhst Du damit den Durchsatz. Was aber, wenn die zweite Quelle sehr schnell ist?

Dazu braucht man die Situation nur einmal umzudrehen: man lädt etwas von einer sehr schnellen Quelle herunter, erreicht also maximale Übertragungsrate. Dann bringt ein zweiter Download nichts. Selbst, wenn sich an den Übertragungszeiten der beiden Dateien separat betrachtet nichts ändert, sorgt der zusätzlich Download nur dafür, dass ggf. beide Dateien erst später vollständig zur Verfügung stehen, selbst wenn die Gesamtzeit kürzer ist als die Summe der Einzelzeiten.

Latenzzeiten gibt es praktisch immer. Neben der Antwortzeit des Servers spielt insbesondere die Größe des TCP Fensters eine Rolle: mehr als die dort angegebenen Bytes können nicht empfangen und gesendet werden, ohne der Gegenstelle Antwort zu geben. bzw. auf diese Antwort zu warten.
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben