Ich versuche, ein Design für einen Threadpool mit vielen Designanforderungen für meinen Job zu entwickeln. Dies ist ein echtes Problem für die Arbeit mit Software, und es ist eine schwierige Aufgabe. Ich habe eine funktionierende Implementierung, aber ich würde diese gerne an SO weitergeben und sehen, welche interessanten Ideen die Leute haben können, damit ich mich mit meiner Implementierung vergleichen und sehen kann, wie sie sich zusammensetzt. Ich habe versucht, so spezifisch wie möglich zu den Anforderungen zu sein.
Der Thread-Pool muss eine Reihe von Aufgaben ausführen. Die Aufgaben können kurz ausgeführt (& lt; 1 s) oder lang (Stunden oder Tage) ausgeführt werden. Jede Aufgabe hat eine zugeordnete Priorität (von 1 = sehr niedrig bis 5 = sehr hoch). Aufgaben können zu jeder Zeit eintreffen, während die anderen Aufgaben ausgeführt werden. Wenn sie ankommen, muss der Thread-Pool diese abholen und sie planen, sobald Threads verfügbar werden.
Die Aufgabenpriorität ist völlig unabhängig von der Aufgabenlänge. Tatsächlich ist es unmöglich zu sagen, wie lange eine Aufgabe dauern kann, ohne sie auszuführen.
Einige Tasks sind CPU-gebunden, während andere stark IO-gebunden sind. Es ist unmöglich vorherzusagen, was eine bestimmte Aufgabe sein würde (obwohl ich denke, dass es möglich ist, während der Ausführung der Aufgaben zu erkennen).
Das Hauptziel des Thread-Pools ist die Maximierung des Durchsatzes. Der Thread-Pool sollte die Ressourcen des Computers effektiv verwenden. Im Idealfall wäre die Anzahl der aktiven Threads für CPU-gebundene Tasks gleich der Anzahl der CPUs. Bei E / A-gebundenen Tasks sollten mehr Threads zugewiesen werden als bei CPUs, so dass die Blockierung den Durchsatz nicht übermäßig beeinflusst. Es ist wichtig, die Verwendung von Schlössern zu minimieren und fadensichere / schnelle Behälter zu verwenden.
Im Allgemeinen sollten Sie Tasks mit höherer Priorität mit einer höheren CPU-Priorität ausführen (ref: SetThreadPriority). Tasks mit niedrigerer Priorität sollten nicht verhindern, dass Tasks mit höherer Priorität ausgeführt werden. Wenn also Tasks mit höherer Priorität ausgeführt werden, während Tasks mit niedriger Priorität ausgeführt werden, wird der Task mit der höheren Priorität ausgeführt.
Den Tasks ist ein Parameter "max running tasks" zugeordnet. Jede Art von Aufgabe darf höchstens gleichzeitig mehrere Instanzen der Aufgabe ausführen. Zum Beispiel könnten wir die folgenden Aufgaben in der Warteschlange haben:
Eine funktionierende Implementierung konnte nur (maximal) 1 A, 1 B und 1 C gleichzeitig ausführen.
Es muss unter Windows XP, Server 2003, Vista und Server 2008 (neueste Service Packs) ausgeführt werden.
Als Referenz könnten wir die folgende Schnittstelle verwenden:
%Vor%Was werden wir als Grundbaustein dafür wählen? Windows hat zwei vielversprechende Bausteine: - I / O Completion Ports (IOCPs) und Asynchronous Procedure Calls (APCs). Beides gibt uns FIFO-Warteschlangen, ohne explizites Sperren durchführen zu müssen, und mit einer gewissen Menge integrierter Betriebssystemunterstützung an Orten wie dem Scheduler (zum Beispiel können IOCPs einige Kontextwechsel vermeiden).
APCs passen vielleicht etwas besser, aber wir müssen etwas vorsichtig mit ihnen sein, weil sie nicht ganz "transparent" sind. Wenn das Arbeitselement eine warnbare Wartezeit (:: SleepEx, :: WaitForXxxObjectEx usw.) ausführt und wir versehentlich eine APC an den Thread senden, übernimmt die neu abgehende APC den Thread und unterbricht die zuvor ausgeführte APC, bis die neue APC vorhanden ist fertig. Das ist schlecht für unsere Parallelitätsanforderungen und kann Stack-Überläufe wahrscheinlicher machen.
Es muss unter Windows XP, Server 2003, Vista und Server 2008 (neueste Service Packs) ausgeführt werden.
Welche Funktion der integrierten Thread-Pools des Systems macht sie für Ihre Aufgabe ungeeignet? Wenn Sie auf XP und 2003 abzielen, können Sie die neuen glänzenden Vista / 2008-Pools nicht verwenden, aber Sie können weiterhin QueueUserWorkItem und Freunde verwenden.
@DrPizza - das ist eine sehr gute Frage, und eine, die direkt auf das Herz des Problems trifft. Es gibt ein paar Gründe, warum QueueUserWorkItem und der Windows NT-Thread-Pool ausgeschlossen wurden (obwohl die Vista-Version interessant aussieht, vielleicht in ein paar Jahren).
Erstens wollten wir eine bessere Kontrolle darüber haben, wann es startet und Threads stoppt. Wir haben gehört, dass der NT-Threadpool nur ungern einen neuen Thread startet, wenn er denkt, dass die Tasks kurz ausgeführt werden. Wir könnten die WT_EXECUTELONGFUNCTION verwenden, aber wir haben wirklich keine Ahnung, ob die Aufgabe lang oder kurz ist.
Zweitens, wenn der Thread-Pool bereits mit lang laufenden Tasks mit niedriger Priorität gefüllt war, gab es keine Chance, dass eine Aufgabe mit hoher Priorität rechtzeitig ausgeführt wurde. Der NT-Thread-Pool hat kein wirkliches Konzept von Aufgabenprioritäten, daher können wir kein QueueUserWorkItem machen und sagen "Oh, übrigens, führe dieses sofort aus".
Drittens (laut MSDN) ist der NT-Threadpool nicht mit dem STA-Apartmentmodell kompatibel. Ich bin mir nicht ganz sicher, was das bedeuten würde, aber alle unsere Worker-Threads laufen in einer STA.
@DrPizza - das ist eine sehr gute Frage, und eine, die direkt auf das Herz des Problems trifft. Es gibt ein paar Gründe, warum QueueUserWorkItem und der Windows NT-Thread-Pool ausgeschlossen wurden (obwohl die Vista-Version interessant aussieht, vielleicht in ein paar Jahren).
Ja, es sieht so aus, als wäre es in Vista ziemlich aufgepeppt worden, ziemlich vielseitig jetzt.
OK, mir ist immer noch nicht ganz klar, wie Sie sich die Prioritäten wünschen. Wenn der Pool gerade eine Aufgabe vom Typ A mit maximaler Gleichzeitigkeit von 1 und niedriger Priorität ausführt, und ihm eine neue Aufgabe auch vom Typ A (und maximale Nebenläufigkeit 1) gegeben wird, aber dieses Mal mit einer hohen Priorität, was sollte er tun? ?
Das Aussetzen des gerade ausgeführten A ist haarig (es könnte eine Sperre enthalten, die der neue Task ausführen muss, um das System zu blockieren). Es kann keinen zweiten Thread erzeugen und ihn nur nebeneinander laufen lassen (die erlaubte Nebenläufigkeit ist nur 1). Es kann jedoch nicht warten, bis die Aufgabe mit niedriger Priorität abgeschlossen ist, da die Laufzeit unbegrenzt ist, und dies würde es einer Aufgabe mit niedriger Priorität ermöglichen, eine Aufgabe mit hoher Priorität zu blockieren.
Meine Annahme ist, dass es das letztere Verhalten ist, nach dem Sie suchen?
@DrPizza:
Okay, ich weiß immer noch nicht genau, wie Sie wünschen, dass die Prioritäten funktionieren. Ob Der Pool führt derzeit eine Aufgabe aus vom Typ A mit maximaler Parallelität von 1 und niedrige Priorität, und es wird gegeben eine neue Aufgabe auch vom Typ A (und maximal Nebenläufigkeit 1), aber dieses Mal mit a hohe Priorität, was sollte es tun?
Das hier ist ein bisschen schwierig, obwohl ich in diesem Fall denke, dass ich glücklich sein würde, dass die Aufgabe mit niedriger Priorität einfach ausgeführt werden kann. Normalerweise würden wir nicht viele Aufgaben mit unterschiedlichen Thread-Prioritäten sehen. In unserem Modell ist es tatsächlich möglich, Aufgaben an bestimmten, genau definierten Punkten (aus verschiedenen Gründen) anzuhalten und später neu zu starten, obwohl die damit verbundenen Komplikationen das Risiko wahrscheinlich nicht wert sind.
Normalerweise hätten nur unterschiedliche Arten von Aufgaben unterschiedliche Prioritäten. Zum Beispiel:
Unter der Annahme, dass die A-Aufgaben gekommen waren und ausgeführt wurden, waren die B-Aufgaben angekommen. Wir wollten, dass die B-Aufgaben mehr oder weniger sofort ausgeführt werden konnten.
Tags und Links c++ multithreading windows