Windows-Sockets - Annehmen und Behandeln in getrennten Threads

1. Kleiner Tipp: Dieses übertriebene GROSSSCHREIBEN wirkt nicht gerade freundlich und muss meiner Meinung nach auch nicht sein.
Sorry dafür. Das ist mir auch bewusst, aber ich war zu dem Zeitpunkt entnervt da mir der Post eher als Behauptung und weniger als konstruktive Kritik vorkam. Zumal ich mit dem bereits geposteten Code einen bisher fehler- und problemfreien Webserver am laufen habe, der ohne Probleme stundenlang 300 Requests gleichzeitig beantworten kann (getestet mit ApacheBench)
Und nur damit ihr das nicht falsch versteht: Ich freu mich wirklich sehr darüber dass hier doch noch ein paar Leute reingefunden haben und konstruktive Voschläge bringen!

2. Zu volatile:
Threadsicher ist das nicht.
Nun mit diesem Thema habe ich mich extra lange beschäftigt, daher habe ich mich auch dafür entschieden. volatile ist nicht generell threadsicher, dass ist richtig. Aber im Zusammenhang mit einem boolschen-Typen bahaupte ich jetzt einfach mal: Das das in jedem Fall threadsicher sein wird. Bitte überzeugt mich vom Gegenteil.

Ja müsstest du, was allerdings keinerlei Aufwand ist, da die meiste Zeit sowieso beim Aufruf von select beim Warten auf "Events" vergehen wird. Also etwa so
Code:
select
   if server in readables:
       accept()
   for(client in clients)
       if client in readables:
          client.handle_read()
       if client in writables:
          client.handle_write()
Wenn "handle_read()" und "handle_write()" ein Event feuern oder einen Thread starten, dann gebe ich dir damit recht. Wenn ich jetzt aber in "handle_read()" z.B. die HTTP-Header lese und auswerte, dann vergeht Zeit in der ein anderer Client warten muss. Und wenn jetzt beispielsweise durch "handle_write()" sehr große Datenmengen an einen Client senden würden, müsste ein anderer Client sogar eherblich lange warten.
So empfange, verwerte und sende ich in einem gesonderten Thread, und kann in einem anderen neue Clienten annehmen und auf den den Stack zu erledigender Aktionen packen. Oder hab ich dich immer noch missverstanden?

Abhängig vom verwendeten OS kann man für solche Sachen die entsprechenden atomaren Funktionen verwenden. Bei Windows wäre dasInterlockedCompareExchange
Guter Einwand, dass werde ich mal einbauen. Aber ist das an dieser Stelle wirklich so viel besser als das volatile bool? Ich hab das Problem dabei glaub ich noch nicht so ganz verstanden.

Ich hoffe das die Diskussion so angeregt weitergeht...

Mfg Pain-maker
 
Zuletzt bearbeitet:
Wenn "handle_read()" und "handle_write()" ein Event feuern oder einen Thread starten, dann gebe ich dir damit recht. Wenn ich jetzt aber in "handle_read()" z.B. die HTTP-Header lese und auswerte, dann vergeht Zeit in der ein anderer Client warten muss. Und wenn jetzt beispielsweise durch "handle_write()" sehr große Datenmengen an einen Client gesendet würden, müsste ein anderer Client sogar eherblich lange warten.
So empfange, verwerte und sende ich in einem gesonderten Thread, und kann in einem anderen neue Clienten annehmen und auf den den Stack zu erledigender Aktionen packen. Oder hab ich dich immer noch missverstanden?
Ich hoffe das die Diskussion so angeregt weitergeht...
Mfg Pain-maker

Ja du hast mich immer noch missverstanden.
die read + write Funktionen für das Socket sind nicht blockend. wen ein handle_read aufgerufen wird, dann wird nicht blockend gelesen. Das nimmt keine Zeit in anspruch. Das Auswerten des HTTP Headers sowie das generieren einer Antwort dauert ebenfalls keine wirklich messbare Zeit. Zu dem Zeitpunkt sendest du selbst noch garnichts, sondern speicherst die Antwort in einem "writebuffer" und reihst das Socket in die Liste der Sockets ein, für die du handle_write-Events empfangen willst.
Beim nächsten select, wenn das Socket ohne blockieren schreibbar ist, empfängt es ein handle_write-Event. Dort schreibst du aus deinem Buffer bzw. Danach direkt aus der Datei, die ausgeliefert werden soll. Du schreibst nur soviel, wie du ohne zu blockieren kannst. (send gibt dir die bytes zurück, die es von den übergebenen geschrieben hat). Beim nächsten handle_write, wenn also das Socket wieder schreibbar ist, kannst du dann weiter schreiben.
So werden alle Anfragen doch parallel abgearbeitet, ohne dass du mehr als einen Thread benötigst.
 
Nun mit diesem Thema habe ich mich extra lange beschäftigt, daher habe ich mich auch dafür entschieden. volatile ist nicht generell threadsicher, dass ist richtig. Aber im Zusammenhang mit einem boolschen-Typen bahaupte ich jetzt einfach mal: Das das in jedem Fall threadsicher sein wird. Bitte überzeugt mich vom Gegenteil.
Mit dem Datentyp hat das im allgemeinen nichts zu tun. Ein bool kann zwar mit hoher Wahrscheinlichkeit in einer atomaren Operation gelesen und geschrieben werden (auch wenn das keineswegs garantiert ist), aber ein allgemeines threadsicheres Mutex kannst du allein mit einem volatile bool nicht umsetzen.

Grüße,
Matthias
 
Bitte überzeugt mich vom Gegenteil.

Das wird dich früher oder später deine Applikation, wenn du nahezu unauffindbare Fehler hast.

Aber ist das an dieser Stelle wirklich so viel besser als das volatile bool? Ich hab das Problem dabei glaub ich noch nicht so ganz verstanden.

Es ist nunmal so, dass der Standard nichts über die Grösse der Schritte aussagt, die eine CPU (bzw. das OS) pro Thread machen muss bis sie zum nächsten Thread überspringt. Aus diesem Grund kannst du dich diesbezüglich auf nichts verlassen, was lediglich auf dem C oder C++-Standard beruht. Sowohl der Compiler als auch dessen Optimierer und vor allem das OS ist völlig frei zu tun, was es will. Wenn es aus Optimierungsgründen plötzlich entscheidet den Thread X, der in seinen Augen eine kleine Priorität hat nur noch in sehr kleinen Schritten und in grösseren Zeitabständen abzuarbeiten und dafür Thread Y, der aus welchen Gründen auch immer (die teilweise auch gesteuert werden können durch gewisse typische Konstrukte) für das OS als wichtiger und dringender scheint, forciert kann es sehr schnell mal möglich werden, dass in Thread X ein Write eines bools (oder auch der Read) nicht mehr atomar sind, in Thread Y jedoch schon.

Willst du diesbezüglich Gewissheit haben so wirst du darauf angewiesen sein dich mit den Funktionen des Betriebssystems auseinanderzusetzen und nur diejenigen Funktione, die explicit als atomar auf dem OS gekennzeichnet werden kannst du mit Sicherheit (zumindest theoretisch) als threadsicher annehmen. Denn wie oben gesagt, im Endeffekt entscheidet das OS selber, was wann geschieht.
 
Zurück