2009-08-10 6 views
2

Wir erstellen eine C# -App, die über TCP/IP-Sockets mit einem anderen System kommunizieren muss. Wir erwarten ungefähr 1-2 eingehende Transaktionen pro Sekunde, wobei jede Nachricht durchschnittlich etwa 10k groß ist (Text und 1 Bild).Einzel- oder Multithread mit TCPListener-Klasse?

Wir werden dann etwas verarbeiten (könnte je nach einigen Variablen zwischen 100 Millisekunden und 3 Sekunden dauern) und dann eine Antwort von etwa 1k senden.

In den Beispielen, die ich angeschaut habe, sind einige multithreaded. Wäre es für diese Anwendung besser, Single- oder Multi-Threading zu machen? Wenn Multithreading empfohlen wird, was würden die verschiedenen Threads ungefähr tun?

Antwort

3

Wenn Sie mehrere Verbindungen erwarten, benötigen Sie mehrere Threads. Jeder Thread erhält Streams für einen bestimmten Client, die separat behandelt werden müssen.

Ich denke, die Silverlight policy server ist ein großartiges Beispiel für eine Multithread-Server-App. Es verwendet jedoch die Socket-Klasse anstelle von TcpListener.

+0

Würden Sie sagen, dass viele Verbindungen auf demselben Port vom selben Client aus "mehrere Verbindungen" oder nur eine Verbindung bilden? – alchemical

+0

Es gilt immer noch mehrere Verbindungen. –

5

(nicht spezifisch für C#)

Nachdem es in beiden Richtungen erfolgen (extreme Leistung war nicht der entscheidende Faktor), ich ziehe den 1-thread-per-Verbindung Ansatz.

Listener-Thread
Die Aufgabe dieses Threads ist auf der Buchse für eingehende Verbindungen zu hören, akzeptieren sie, und laichen eine neue Anschlussgewinde (es die angeschlossene Buchse geben).

Verbindungsgewinde
Diese Fäden (ein pro-Verbindung) mit dem angeschlossenen Buchse gesamte Kommunikation handhaben. Sie können auch die Verarbeitung von Anfragen bearbeiten, wenn sie synchron sind (Sie müssen dies für Ihre spezifische Anwendung prüfen).

Wenn die Verbindung stirbt, stirbt auch dieser Gewinde.

Management-Themen
Wenn Bereinigung oder regelmäßige Wartung notwendig durchgeführt, diese können alle laufen in ihrer eigenen Threads.

Denken Sie nur daran, Sperren (offensichtlich): Wie viel Daten müssen die Verbindungen teilen? Stellen Sie sicher, dass beim Zugriff alle Ressourcen korrekt gesperrt sind und Sie keine Deadlocks oder Race Conditions haben. Das ist jedoch eher ein "allgemeines Threading" -Thema.

+3

Verwenden Sie Threadpools, anstatt für jede Anforderung einen neuen Thread zu generieren. Die Erstellung eines neuen Threads kann teuer sein und bietet gute Möglichkeiten für DOS-Angriffe. – CsTamas

+0

Es wird ein paar gemeinsam genutzte Ressourcen geben: ein Datenbankverbindungsobjekt, ein Protokollierungsobjekt usw. Dies sind im Allgemeinen thread-sichere Singletons, aber ich könnte auch jedem Thread eine eigene Instanz geben ... – alchemical

+0

Wenn extreme Leistung nicht dein war Hauptüberlegung - Was hat Sie dazu gebracht, mehrere Threads zu bevorzugen? – alchemical

2

Ich würde Sockets akzeptieren und Async-Aufrufe verwenden. Ermöglicht die Annahme mehrerer Verbindungen und vermeidet das Erstellen eines Threads für jede Verbindung.

im Grunde eine Buchse mit dem

Sockel Sockel = tcpListener.AcceptSocket() Zuhörers erzeugen;

und Socket.BeginReceive, um Daten zu empfangen.

0

Ich denke, es ist wichtig zu definieren, was mit dem Wort "Verbindung" gemeint ist.'

Wenn das andere System jedes Mal, wenn eine Transaktion gesendet wird, eine neue Verbindung zu Ihrem TcpListener erstellt, würde dies als mehrere Verbindungen betrachtet werden, und es wäre sinnvoll, einen dedizierten Thread zur Verarbeitung dieser eingehenden Verbindungsanforderungen zu verwenden. Wenn dies der Fall ist, ignorieren Sie alles darüber hinaus und verwenden Sie die Vorschlagslösung von gahooa.

Auf der anderen Seite, wenn das andere System einfach eine Verbindung herstellt und alle Transaktionen über dieselbe Verbindung sendet, dann hat es wirklich keinen Sinn, die Verbindungsanforderung in einem separaten Thread zu verarbeiten (da es nur eine Verbindung gibt). Wenn dies der Fall ist, würde ich vorschlagen, die eingehende Anfrage einmal zu akzeptieren und den Socket asynchron zu lesen (im Gegensatz zum Abfragen des Sockets nach Daten). Jedes Mal, wenn Sie eine vollständige Transaktion erhalten, werfen Sie sie "über die Mauer" zu einem Transaktionsverarbeitungsthread. Wenn die Verarbeitung abgeschlossen ist und die "Antwort" berechnet ist, werfen Sie sie "über die Wand" zu einem Antwort-Thread, der das Ergebnis an das andere System zurücksendet. In diesem Szenario gibt es grundsätzlich vier Threads:

  1. Hauptthread
  2. lesen Gewinde
  3. Verarbeitung Gewinde
  4. schreiben Gewinde

Der Haupt-Thread erstellt die TcpListener und wartet, bis die Verbindung ist festgelegt. An diesem Punkt wird das asynchrone Lesen gestartet und gewartet, bis das Programm beendet wird (ManualResetEvent.WaitOne()).

Der Lese-Thread ist der asynchrone Thread, der das Lesen vom NetworkStream unterstützt.

Der Verarbeitungsthread empfängt Transaktionen vom Thread "Lesen" und führt alle erforderlichen Verarbeitungen aus.

Der Write-Thread übernimmt alle Antworten, die vom Verarbeitungsthread generiert werden, und schreibt sie in den NetworkStream.

Entsprechend dieser link, müssen Sie das Lesen und Schreiben aus dem gleichen NetworkStream nicht synchronisieren, solange das Lesen und Schreiben in separaten Threads erfolgt. Sie können eine allgemeine Liste oder Warteschlange verwenden, um Daten zwischen den Threads zu verschieben. Ich würde eine für die Read-to-Processing-Daten und eine für die Processing-to-Write-Daten erstellen. Stellen Sie sicher, dass Sie den Zugriff auf sie mithilfe der SyncRoot-Eigenschaft synchronisieren.