Cross-thread calls
-
Also mach eine Queue in die die Worker-Threads reinschreiben und wo der Main-Thread rausliest.
Oder du lagerst das Schreiben von Antworten in einen eigenen Thread aus, der sonst nix macht.
Dann hätte jeder Thread genau eine Aufgabe.Der Main-Thread liest SCGI Requests vom Webserver und erzeugt die Worker-Threads (bzw. verteilt die Requests an gepoolte Worker-Threads - was auch immer).
Die Worker-Threads bauen aus den Requests Antworten und stecken sie in die Antwort-Queue.
Und der Antwort-Schreibe-Thread nimmt die Antworten aus der Antwort-Queue und schickt sie zurück an den Webserver.Die Queue implementierst du klassisch mit den Funktionen der Threading-API die du verwendest, also mit z.B. Mutex + Condition-Variable, Critical-Section + Event oder was du halt zur Verfügung hast.
Viel einfacher und sauberer als das wird es nicht.
Dass die Mutex Locks dabei blockierend sind sollte egal sein, da die Locks immer nur für einen sehr kurzen Zeitraum gehalten werden. Das .NET Framework macht im Hintergrund auch nix anderes.
-
Grandios - habe mich mal etwas reingelesen ins Pooling und in die Arbeit mit Queues. Dann habe ich irgendwann die Lock-Free-Queues entdeckt, um zwei Stunden später festzustellen, dass sie für meine Situation ein viel zu großes Problem darstellen.

Da Mutexes also weniger schlimm sind als ich bisher immer dachte (ich dachte, dass sie viel länger blockieren), bin ich mit einer solchen Lösung voll zufrieden: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html (siehe unten bei "The Final Code").
Das einzige Problem an dieser Implementierung ist die Benutzung von Boost. Ich möchte diese Library nicht verwenden, auch wenn sie oft als "well-known" und als quasi-standard betrachtet wird.
Gibt es vergleichbare Implementation dazu, ohne Boost? boost::mutex & boost::mutex::scoped_lock sind ja relativ einfach zu ersetzen. Nur wie man boost::condition_variable umsetzt, erschließt sich mir nicht...
-
Gibt es vergleichbare Implementation dazu, ohne Boost?
Ja, klar.
Dazu müsste man aber erstmal wissen was für eine Threading-API du verwendest.
-
Hallo,
grundsätzlich bleibe ich erst einmal auf Windows, und möchte das später auch auf Linux machen (erst einmal zweitrangig). Also für mich wäre eine systemnahe Implementation am interessantesten, um zu lernen, wie ich damit umzugehen habe...
Gruß
-
theliquidwave schrieb:
grundsätzlich bleibe ich erst einmal auf Windows
Dann schau dir die Doku zu
CreateMutex
WaitForSingleObject
ReleaseMutex
an.Ich würde dennoch mit boost threads arbeiten. Spart ne Menge Arbeit und gibt dir genug verstädnis für die Materie. IMHO ist boost thread sogar besser, weil du nicht durch Implementierungsdetails abgelenkt bist. Ein boost mutex ist zB viel schöner und klarer als ein Win32 mutex

-
Hallo,
zu spät - ich habe den Sinn von Condition Variables und Mutexes (bzw. den Vorteil von Critical Sections unter Windows) nun verstanden und konnte daher eigene Wrapper implementieren.
Wenn ich nachher die Möglichkeit habe, das Ganze hochzuladen, werde ich das tun, falls Interesse besteht (und damit eventuell mal einer kurz rüberschauen kann).
Danke an alle

Ich finde es übrigens lustig, dass so etwas Essentielles erst ab Windows Vista verfügbar ist
(also nativ implementiert)
-
theliquidwave schrieb:
Ich finde es übrigens lustig, dass so etwas Essentielles erst ab Windows Vista verfügbar ist
(also nativ implementiert)Was meinst du?
-
Windows Server 2003 and Windows XP: Condition variables are not supported.
-
theliquidwave schrieb:
ich habe den Sinn von Condition Variables und Mutexes (bzw. den Vorteil von Critical Sections unter Windows)
Nur ein Hinweis: Critical Sections unter Windows *sind* Mutexen. Bloss ein anderer Name, und man kann sie halt nicht prozessübergreifend verwenden.
Das was Windows "Mutex" nennt ist ein Kernel-Objekt, und als solches ziemlich langsam.Wenn ich nachher die Möglichkeit habe, das Ganze hochzuladen, werde ich das tun, falls Interesse besteht (und damit eventuell mal einer kurz rüberschauen kann).
Tu das, drüberschauen kann ich auf jeden Fall mal.
Ich finde es übrigens lustig, dass so etwas Essentielles erst ab Windows Vista verfügbar ist
(also nativ implementiert)Naja, Windows hat statt Condition-Variablen sog. "Events" angeboten. Mit denen kann man auch ganz gut arbeiten, allerdings nicht ganz so unkompliziert und problemlos wie mit Condition-Variablen.
Weil auf quasi allen nicht-Windows Systemen aber Condition-Variablen "Standard" sind, und es auch nicht ganz trivial ist Condition-Variablen mit Windows Boardmnitteln (also Critical-Sections, Events etc.) performant und korrekt nachzubilden, hat MS mit Windows Vista endlich "native" Condition-Variablen nachgereicht. Damit Windows auch endlich Condition-Variablen hat, und die Protierung von Projekten auf Windows einfacher wird.
Wenn Support für XP/2003 für dich nicht wichtig ist dann nimm einfach die native Windows Condition-Variablen. Wenn Support für XP/2003 doch wichtig ist, dann würde ich auch zu Boost.Thread, Qt oder einer anderen Library raten, die Condition-Variablen unter Windows XP/2003/... anbietet.
Weil es wie gesagt nicht ganz trivial ist das selbst zu stricken, und eine Queue aus der mehrere Threads lesen ist mit Events auch nicht ganz einfach.
-
Shade Of Mine schrieb:
theliquidwave schrieb:
grundsätzlich bleibe ich erst einmal auf Windows
Dann schau dir die Doku zu
CreateMutex
WaitForSingleObject
ReleaseMutexVerlinkst du absichtlich die Windows XxxMutex Funktionen? Weil die ausser langsam nur langsam sind? Damit du nachher sagen kannst "nimm Boost, dann wird das alles auch viel schneller"? Oder ...?
Oder ist dir nur einfach nicht klar dass es unter Windows Critical-Sections gibt, und dass Critical-Sections Mutexen *sind*, nur eben ... besser als die Kernel-Mutexen?
-
hustbaer schrieb:
Verlinkst du absichtlich die Windows XxxMutex Funktionen?
Es sind Stichworte mit denen man den Rest easy finden kann. Ich haette mir auch raussuchen koennen wie die Critical Sections heissen, war aber dazu zu faul.
Mit der MFC gehts ueber ein CSingleLock aber wie das mit der WinAPI geht, keine Ahnung.
-
Shade Of Mine schrieb:
hustbaer schrieb:
Verlinkst du absichtlich die Windows XxxMutex Funktionen?
Es sind Stichworte mit denen man den Rest easy finden kann. Ich haette mir auch raussuchen koennen wie die Critical Sections heissen, war aber dazu zu faul.
Und weil du zu faul warst EnterCriticalSection() und LeaveCriticalSection() zu ergoogeln hast du lieber Critical-Section gleich gar nicht erwähnt?
Wie macht das denn Sinn?
-
Wenn du denkst dass Information fehlt, dann ergaenze sie einfach.
Ein Forenpost ist kein Fachartikel - es wird immer Verbesserungen geben und man recherchiert keine 3 Tage fuer den Inhalt. Man schreibt einfach was einem einfaellt und versucht zu helfen.Wenn du denkst, dass die Information zu Critical Sections wichtig ist, dann schreib sie einfach hin, no biggy

-
hustbaer schrieb:
Nur ein Hinweis: Critical Sections unter Windows *sind* Mutexen. Bloss ein anderer Name, und man kann sie halt nicht prozessübergreifend verwenden.
Das was Windows "Mutex" nennt ist ein Kernel-Objekt, und als solches ziemlich langsam.Ich weiß, deshalb habe ich ja auch geschrieben:
theliquidwave schrieb:
(bzw. den Vorteil von Critical Sections unter Windows)
Trotzdem danke für den Hinweis

hustbaer schrieb:
Tu das, drüberschauen kann ich auf jeden Fall mal.
Danke, mache ich morgen...
hustbaer schrieb:
Naja, Windows hat statt Condition-Variablen sog. "Events" angeboten. Mit denen kann man auch ganz gut arbeiten, allerdings nicht ganz so unkompliziert und problemlos wie mit Condition-Variablen.
Okay.
hustbaer schrieb:
Weil die ausser langsam nur langsam sind?
In der MSDN steht nur, dass Critical Sections "sligthy faster" sind als Mutexes...
-
theliquidwave schrieb:
hustbaer schrieb:
Weil die ausser langsam nur langsam sind?
In der MSDN steht nur, dass Critical Sections "sligthy faster" sind als Mutexes...
Uff. Ich müsste den Unterschied mal ausmessen. Würde mich aber wundern wenn es nur "sligthy" wäre.
Critical-Sections sind Usermode spin-sleep Locks, also das schnellste was man so an nicht spezialisierten Mutexen bauen kann.
Ne Win32 Mutex ist dagegen wie gesagt ein Kernel-Objekt. D.h. es müssen mehrere Dinge passieren wenn man damit was machen will
- Kernelmode-Transition
- Handle-Lookup & Ermittlung des Handle-Typs
- Security-Check, inklusive Check ob der aufrufende Prozess überhaupt berechtigt ist mit dem übergebenen HANDLE zu arbeiten
- Die eigentliche Aktion
- Transition zurück in den Usermode
-
So. Hab mal nen ganz einfachen Test gemacht - bloss ein Thread, d.h. keine Lock-Contention, einfach nur mal gucken wie schnell man CS vs. Win32-Mutex erzeugen + zerstören kann, dann erzeugen + zerstören mit 1x lock+unlock dazwischen, und dann erzeugen, 10000x lock/unlock dazwischen und dann zerstören:
Mit MSVC 2012 std::chrono::high_resolution_clock (aka. GetSystemTimeAsFileTime()):
class CriticalSectionNoDebugInfo create/destroy = 2929 us (base) class CriticalSectionNoDebugInfo create/lock/unlock/destroy = 8789 us (base) class CriticalSectionNoDebugInfo lock/unlock = 47851 us (base) class CriticalSection create/destroy = 31250 us = 10.6666 * base class CriticalSection create/lock/unlock/destroy = 35156 us = 3.99998 * base class CriticalSection lock/unlock = 46875 us = 0.979593 * base class Win32Mutex create/destroy = 193359 us = 65.9997 * base class Win32Mutex create/lock/unlock/destroy = 308593 us = 35.111 * base class Win32Mutex lock/unlock = 1149414 us = 24.0204 * baseMit my_highres_clock aka. QueryPerformanceCounter():
class CriticalSectionNoDebugInfo create/destroy = 3691 us (base) class CriticalSectionNoDebugInfo create/lock/unlock/destroy = 8498 us (base) class CriticalSectionNoDebugInfo lock/unlock = 46898 us (base) class CriticalSection create/destroy = 30244 us = 8.19254 * base class CriticalSection create/lock/unlock/destroy = 34650 us = 4.07736 * base class CriticalSection lock/unlock = 46955 us = 1.00123 * base class Win32Mutex create/destroy = 192070 us = 52.0267 * base class Win32Mutex create/lock/unlock/destroy = 307289 us = 36.1587 * base class Win32Mutex lock/unlock = 1144865 us = 24.4117 * base"sligthy faster" kommt also nicht so ganz hin. > Faktor 20 ist bei mir nicht mehr "sligthy"

-
hustbaer schrieb:
- Security-Check, inklusive Check ob der aufrufende Prozess überhaupt berechtigt ist mit dem übergebenen HANDLE zu arbeiten
OK, "Security-Check" ist Quatsch, der ist nur nötig wenn man versucht ein neues HANDLE zu bekommen, und das tut man bei WaitForSingleObject/ReleaseMutex ja nicht.
Der Check ob der aktuelle Prozess mit diesem HANDLE arbeiten darf ist hier denke ich schon nötig. HANDLE-Werte sind IIRC ja prozessübergreifend, aber nicht jeder Prozess darf mit jedem HANDLE arbeiten.
-
Krass o_O Danke für's testen

Ich werde den Code noch ein bisschen verfeinern und nachher hochladen.
Aber sei gewarnt, ich code seit 3 Tagen mit Notepad++ ohne jede Chance auf einen Compiler. Ich bin schon gespannt, wenn ich das Projekt zum ersten mal durch einen Compiler schiebe