Ich möchte abhängige Aufgaben über mehrere Flüsse verteilen, die in der Reihenfolge (in jedem Ablauf) verarbeitet werden müssen. Die Flüsse können parallel verarbeitet werden.
Um genau zu sein, nehmen wir an, ich brauche zwei Warteschlangen und möchte, dass die Aufgaben in jeder Warteschlange der Reihe nach ausgeführt werden. Hier ist ein Beispiel-Pseudocode, um das gewünschte Verhalten zu veranschaulichen:
%Vor%Hier ist ein Diagramm mit Pfeilen, die Abhängigkeiten zwischen Arbeitselementen veranschaulichen:
Die Frage ist, wie mache ich das mit C # 4.0 / .NET 4.0? Im Moment habe ich zwei Worker-Threads, einen pro Warteschlange, und ich verwende BlockingCollection<>
für jede Warteschlange. Ich möchte stattdessen den .NET-Thread-Pool nutzen und Worker-Threads Elemente gleichzeitig (über Flüsse), aber seriell innerhalb eines Flusses verarbeiten lassen. Mit anderen Worten möchte ich angeben können, dass zum Beispiel wi1b von der Fertigstellung von wi1a abhängt, ohne die Vervollständigung zu verfolgen und wi1a zu erinnern, wenn wi1b ankommt. Mit anderen Worten, ich möchte nur sagen: "Ich möchte ein Workitem für queue1 einreichen, das seriell mit anderen Elementen verarbeitet werden soll, die ich bereits für queue1 eingereicht habe, aber möglicherweise parallel mit Workitems, die an andere Warteschlangen gesendet wurden".
Ich hoffe, diese Beschreibung ergab einen Sinn. Wenn nicht, dann zögern Sie nicht, Fragen in den Kommentaren zu stellen, und ich werde diese Frage entsprechend aktualisieren.
Danke fürs Lesen.
Aktualisierung:
Um "fehlerhafte" Lösungen zusammenzufassen, hier sind die Lösungen aus dem Antwortbereich, die ich nicht verwenden kann und die Gründe, warum ich sie nicht verwenden kann:
TPL-Tasks erfordern die Angabe der Antezedenz-Task für ein ContinueWith()
. Ich möchte nicht über die Vorgängeraufgabe einer Warteschlange informiert werden, wenn ich eine neue Aufgabe einreiche.
TDF-ActionBlocks sahen vielversprechend aus, aber es sieht so aus, als ob die in einem ActionBlock geposteten Elemente parallel verarbeitet werden. Ich brauche für die Elemente für eine bestimmte Warteschlange seriell verarbeitet werden.
Update 2:
RE: ActionBlocks
Es scheint so, als ob die Option MaxDegreeOfParallelism
auf eins gesetzt wird, um die parallele Verarbeitung von Workitems zu verhindern, die an einen einzelnen ActionBlock
übergeben wurden. Daher scheint es, dass ein ActionBlock
pro Warteschlange mein Problem mit dem einzigen Nachteil behebt, dass dies die Installation und Bereitstellung der TDF-Bibliothek von Microsoft erfordert und ich auf eine reine .NET 4.0-Lösung gehofft habe. Bis jetzt ist dies die Kandidat akzeptierte Antwort, es sei denn jemand kann einen Weg finden, dies mit einer reinen .NET 4.0-Lösung zu tun, die nicht zu einem Worker-Thread pro Warteschlange degeneriert (was ich bereits verwende).
Ich verstehe, dass Sie viele Warteschlangen haben und keine Threads binden wollen. Sie könnten einen ActionBlock pro Warteschlange haben. Der ActionBlock automatisiert das meiste von dem, was Sie benötigen: Er verarbeitet Arbeitsaufgaben seriell und startet nur eine Aufgabe, wenn Arbeit aussteht. Wenn keine Arbeit ansteht, ist keine Aufgabe / kein Thread blockiert.
Am besten verwenden Sie Task Parallel Library (TPL)
und Continuations
. Eine Fortsetzung ermöglicht Ihnen nicht nur das Erstellen eines Aufgabenflusses, sondern auch das Behandeln Ihrer Ausnahmen. Dies ist eine großartige Einführung in die TPL. Aber um dir eine Idee zu geben ...
Sie können einen TPL-Task mit
starten %Vor% Um nun eine zweite Aufgabe zu starten, wenn eine Vorgängeraufgabe beendet wurde (fehlerhaft oder erfolgreich), können Sie die Methode ContinueWith
verwenden
Sobald also task1
abgeschlossen ist, schlägt fehl oder wird abgebrochen task2
'feuert' und beginnt zu laufen. Beachten Sie, dass, wenn task1
abgeschlossen wurde, bevor die zweite Codezeile erreicht wird, task2
so geplant wird, dass sie sofort ausgeführt wird. Das Argument antTask
, das an das zweite Lambda übergeben wird, ist eine Referenz auf die Vorgängeraufgabe. Siehe diesen Link für detailliertere Beispiele ...
Sie können Fortführungsergebnisse auch von der Vorgängeraufgabe
übergeben %Vor%Hinweis. Stellen Sie sicher, dass Sie die Ausnahmebehandlung im ersten Link nachlesen, da dies einen Neuling zu TPL in die Irre führen kann.
Eine letzte Sache, auf die Sie besonders achten sollten, sind Kinderaufgaben. Untergeordnete Aufgaben sind solche, die als AttachedToParent
erstellt werden. In diesem Fall wird die Fortsetzung nicht ausgeführt, bis alle untergeordneten Aufgaben abgeschlossen sind.
Ich hoffe, das hilft.
Bearbeiten: Haben Sie sich ConcurrentCollections
insbesondere die BlockngCollection<T>
. In Ihrem Fall könnten Sie also etwas wie
Eine .NET 4.0-Lösung basierend auf TPL ist möglich, während die Tatsache versteckt wird, dass die Elternaufgabe irgendwo gespeichert werden muss. Zum Beispiel:
%Vor%Dies verwendet eine einzige Sperre für alle Warteschlangen, um die Idee zu veranschaulichen. Im Produktionscode würde ich jedoch eine Sperre pro Warteschlange verwenden, um Konflikte zu reduzieren.
Es sieht so aus, als ob das Design, das Sie bereits haben, gut ist und funktioniert. Ihre Worker-Threads (eine pro Warteschlange) sind lang andauernd. Wenn Sie stattdessen Tasks verwenden möchten, geben Sie TaskCreationOptions.LongRunning
an, damit Sie einen dedizierten Arbeitsthread erhalten.
Aber es ist nicht wirklich notwendig, den ThreadPool hier zu verwenden. Es bietet nicht viele Vorteile für lang andauernde Arbeit.