Freigeben über


Dieser Artikel wurde maschinell übersetzt.

Asynchrone Agenten

Akteurbasierte Programmierung mit der Asynchronous Agents Library

Michael Chu

Mit Mehrkernprozessoren nun alltäglich auf dem Markt von Servern, Desktops, Laptops wurde die Parallelisierung von Code nie wichtiger. Um diesen wichtigen Bereich zu beheben, stellt Visual Studio 2010 verschiedene neue Methoden C++ Devel ­ Opers diese Funktionen mit einem neuen parallele Common Language Runtime und die neuen Programmiermodellen Parallel nutzen zu können. Jedoch ist eine der wichtigsten Hürde für Entwickler, die Links Entscheidung, welches Programmiermodell für Ihre Anwendungen korrekt ist. Das richtige Modell zugrunde liegenden Parallelität möglicherweise erheblich ausnutzen, aber möglicherweise auch eine Rethink des Aufbaus des Programms und tatsächlich ausgeführt wird.

Die am häufigsten verwendeten parallele Programmiermodelle umfassen heute Container für allgemeine, Parallelität berücksichtigen und Algorithmen wie parallelisieren Schleife Iterationen. Während diese herkömmlichen Verfahren eine leistungsstarke Methode zum Skalieren Anwendungen auf einem Mehrkerncomputer Nutzen sein können, berücksichtigen nicht Sie eine andere wesentlichen Faktoren beeinflussen parallele Leistung: die wachsende Auswirkungen der Latenz. Techniken für die Parallelisierung beschleunigen von Berechnungen und verteilt Sie über mehrere Kerne, zeigt der Amdahl Gesetz (wikipedia.org/wiki/Amdahl's_law ) uns, dass die Leistungssteigerung durch den langsamsten Teil der Ausführung begrenzt ist. In vielen Fällen ist ein zunehmender Prozentsatz der Wartezeit auf e/A-Daten wie z. B. Datenträger oder Netzwerke.

Schauspieler-basierte Programmiermodelle befassen sich mit Problemen wie Wartezeit recht gut und zuerst in den frühen siebziger Jahren ausnutzen, die Ressourcen stark parallelen Computern mit Hunderten oder Tausenden von unabhängigen Prozessoren eingeführt wurden. Das grundlegende Konzept hinter einem Akteur-Modell ist, die Komponenten einer Anwendung als einzelne Akteuren zu behandeln, die durch das Senden, empfangen und Verarbeiten von Nachrichten mit der Welt interagieren können.

Mit der Fülle von Mehrkernprozessoren, mehr kürzlich hat das Actor-Modell als eine effektive Methode zum Ausblenden von Wartezeiten für die effiziente parallele Ausführung resurfaced. Visual Studio 2010 wird die asynchrone Agents Library (AAL), eine aufregende neue Akteur basierendes Modell mit Schnittstellen der Weitergabe von Nachrichten, in dem die Agents die Akteure sind, eingeführt. AAL kann Entwickler Ihre Anwendungen in einer Weise mehr Datenfluss-zentrierten entwerfen. Ein solcher Entwurf wird in der Regel für den produktiven Einsatz von Wartezeit beim Warten auf Daten.

In diesem Artikel werden wir bieten einen Überblick über die AAL und veranschaulichen, wie Sie es in Ihren Anwendungen nutzen können.

Die Laufzeit Parallelität

Die Grundlage für die Unterstützung von Parallelität in Visual Studio 2010 und AAL ist die neue Parallelität Runtime, die im Rahmen von der C Runtime (CRT) in Visual Studio 2010 geliefert wird. Die Parallelität Runtime bietet ein kooperativer Taskplaner und ein Ressourcen-Manager, der ein tiefgreifendes Verständnis der zugrunde liegenden Ressourcen des Computers hat. Dadurch wird die Common Language Runtime zum Ausführen von Aufgaben in einer Weise Lastenausgleich auf einem Mehrkerncomputer.

Abbildung 1 zeigt einen Überblick über die Unterstützung in Visual Studio 2010 für Parallelität in systemeigenem Code. Der Taskplaner ist die Hauptkomponente, die bestimmt, wann und wo Aufgaben ausführen. Es nutzt Informationen vom Ressourcen-Manager die Ausführung Ressourcen optimal zu nutzen. Anwendungen und Bibliotheken selbst arbeiten hauptsächlich mit der Parallelität Runtime über zwei Programmiermodelle, die über den Taskplaner, der AAL und die parallele Muster Library (PPL), befinden, obwohl Sie auch direkt mit der Runtime interagieren können.

image: The Concurrency Runtime

Abbildung 1 die Parallelität Runtime

Die PPL bietet die herkömmlichen Parallelisierung Techniken, z. B. Parallel_for und Parallel_for_each-Konstrukte, Common Language Runtime-Unterstützung Sperren und gleichzeitige Datenstrukturen wie Warteschlangen und Vektoren. Beim nicht den Schwerpunkt dieses Artikels ist die PPL ein leistungsfähiges Tool für Entwickler, die in Verbindung mit der AAL eingeführten neuen Methoden verwendet werden kann. Weitere Informationen über die PPL finden Sie in der Ausgabe vom Februar 2009 der Windows mit C++-Rubrik (msdn.microsoft.com/magazine/dd434652 ).

Im Gegensatz dazu stellt die AAL die Möglichkeit, Anwendungen, die auf einer höheren Ebene und aus einer anderen Perspektive als herkömmliche Techniken zu parallelisieren. Entwickler müssen überlegen Sie Anwendungen aus der Perspektive der Daten, die verarbeitet werden, und entscheiden, wie die Verarbeitung der Daten getrennt werden kann, in Komponenten oder Phasen, die parallel ausgeführt werden können.

Die AAL bietet zwei Hauptkomponenten: ein Weiterleiten von Nachrichten-Framework und asynchrone Agents.

Das Weiterleiten von Nachrichten-Framework enthält eine Reihe von Nachricht blockiert, erhalten, Prozess und Nachrichten übermitteln kann. Durch Verkettung zusammen Nachricht blockiert und der Arbeit Pipelines können erstellt werden, die gleichzeitig ausführen können.

Asynchrone Agents sind die Akteure, die durch das Empfangen von Nachrichten, lokale Arbeit für Ihre eigenen verwalteten Zustand ausführt und Senden von Nachrichten mit der Welt interagieren.

Diese beiden Komponenten ermöglichen zusammen, Entwickler, Parallelität hinsichtlich der der Datenfluss auszunutzen, anstatt der Fluss des Steuerelements und eine bessere tolerieren Wartezeiten durch parallele Ressourcen effizienter Nutzung.

Meldung übergeben Framework

Die erste wichtige Komponente der AAL ist das Weiterleiten von Nachrichten-Framework, ein Satz von Konstrukten Datenfluss-Netzwerke arbeiten pipeline für Entwicklung. Pipelining Arbeit ist eine grundlegende des Datenflusses dar, da es ermöglicht, Daten parallel verarbeitet werden, sobald die Daten bereit, sind bis die Arbeit in mehreren unabhängigen Phasen aufteilen. Nach Abschluss die Verarbeitung von Daten in einem Schritt kann dieser Phase die Daten an die nächste Stufe übergeben während die erste für neue Daten auf dem sucht Sie arbeiten.

Beispielsweise sollten Sie in einer e-Mail-Anwendung, die Sie für ungeeignete Inhalte censors und formatiert, ausgehende Nachrichten. Der Code für diese Art von Vorgang ist hier dargestellt:

std::foreach(reader.begin(); reader.end(); 
  [](const string& word) { 
    auto w1 = censor(word); 
    auto w2 = format(w1); 
    writer.write_word(w2);
  });

Für jedes Wort in der E-mail muss die Anwendung überprüfen, ob er vorhanden, in einem Wörterbuch censored Wörter, die Sie ersetzen ist, wenn dies der Fall ist. Der Code formatiert dann jedes Wortes nach einer Gruppe von Richtlinien.

Es ist Asignificant inhärenten Parallelismus in einem solchen Szenario. Herkömmliche Verfahren für Parallelität fallen jedoch kurz. Ein einfacher Ansatz beispielsweise dann über die Zeichenfolgen in der Text Censor Parallel_for_each-Algorithmus verwendet werden, und formatieren Sie Sie.

Die erste primäre Abschreckung, eine solche Lösung ist, dass die gesamte Datei lesen muss, damit die Arbeit ordnungsgemäß ein Iterator unterteilen kann.
Erzwingen die gesamte Datei zu lesen wird der Prozess e/A-gebundenen und Gewinne für die Parallelisierung verringern kann. Natürlich können Sie einen intelligenten Iterator zu einer Überlappung der Verarbeitung von Wörtern mit die Eingabe gelesen.

Das zweite ernstes Problem mit einem herkömmlichen Parallelisierung Ansatz ist bestellen. Natürlich bei der e-Mail-Nachricht, parallele Verarbeitung des Textes muss die Reihenfolge des Textes beibehalten, oder die Bedeutung der Nachricht ist vollständig verloren. Um die Anordnung des Textes zu gewährleisten, würde eine Technik Parallel_for_each erheblichen Aufwand hinsichtlich der Synchronisierung und Zwischenspeicherung, entstehen, der automatisch durch den AAL behandelt wird.

Die Nachricht in einer Pipeline verarbeitet, können Sie diese beiden Probleme vermeiden, während weiterhin nutzen Parallelisierung. Sollten Sie die Abbildung 2 , in dem eine einfache Pipeline erstellt wurde. In diesem Beispiel werden die Hauptaufgaben der Anwendung – censoring und Formatierungen – sind in zwei Phasen aufgeteilt. In der erste Phase eine Zeichenfolge annimmt und sucht in einem Wörterbuch censored Wörter. Wenn eine Übereinstimmung gefunden wird, wird der Block Censor die Zeichenfolge durch ein anderes Wort aus dem Wörterbuch ersetzt. Andernfalls wird die gleiche Nachricht, die gelangt wurde ausgegeben. Ebenso im zweiten Schritt des Format-Blocks in jedem Wort akzeptiert und ordnungsgemäß formatiert für eine bestimmte Formatvorlage.

image: E-mail Processing Pipeline

Abbildung 2 E Processing Pipeline

In diesem Beispiel kann der Datenfluss Ansatz auf verschiedene Weise nutzen. Da die Anforderung zum Lesen der gesamten Nachricht vor der Verarbeitung entfernt, können die Zeichenfolgen in der Nachricht sofort starten streaming über die censoring und Formatierung von Phasen. Zweitens kann die Pipelineverarbeitung einer Zeichenfolge von der Block Format verarbeitet werden, während die nächste Zeichenfolge Censor-Block bearbeitet wird. Schließlich Da Zeichenfolgen in der Reihenfolge, in den ursprünglichen Text angezeigt verarbeitet werden, muss keine weitere Synchronisierung erfolgen.

Nachricht blockiert

Die Nachrichten blockiert Prozess zu erhalten, speichern und Übertragen von Nachrichten. Nachricht blockiert werden drei Möglichkeiten: Quellen, Ziele und Propagators. Datenquellen haben nur die Möglichkeit, die Weitergabe von Nachrichten, während der Ziele können empfangen, gespeichert und verarbeitet werden. Die Mehrzahl der Blöcke sind Propagators, die beide Quellen und Ziele sind. Mit anderen Worten, haben Sie die Möglichkeit erhalten, speichern und Verarbeiten von Nachrichten auch, um aktivieren und senden diese Nachrichten.

Die AAL enthält eine Reihe von Message Block primitive Typen, die die meisten Anwendungsfälle für Entwickler. Abbildung 3 zeigt eine kurze Übersicht über alle Message-Blöcke in der AAL enthalten. Jedoch blockieren die Modell bleibt geöffnet, wenn Ihre Anwendung einen Message Block mit einem bestimmten Verhalten erforderlich ist, einen benutzerdefinierten geschrieben werden kann, die interagieren können mit den vordefinierten Blöcken. Jeder Block verfügt über einen eigenen eindeutigen Merkmale für das verarbeiten, speichern und Weitergabe von Nachrichten.

Abbildung 3 AAL Message Blocks

Nachrichten blockieren Zweck
UNBOUNDED_BUFFER <Type> Speichert eine unbegrenzte Anzahl von Nachrichten und zu den Zielen propagiert.
Overwrite_buffer <Type> Speichert eine einzelne Nachricht, die jedes Mal überschrieben werden, wird eine neue Nachricht an Sie weitergegeben, und übermittelt es an seine Ziele.
Single_assignment <Type> Speichert eine einzelne Nachricht, die Schreibzugriff ist - once, und es auf seine Ziele übermittelt.
Transformator < Eingabe, Ausgabe > Akzeptiert eine Nachricht des Typs Input und führt eine vom Benutzer bereitgestellte Funktion es auf eine Nachricht des Typs Ausgabe umzuwandeln. Diese transformierten Nachricht wird an seine Ziele weitergeleitet.
Rufen Sie <Type> Akzeptiert eine Nachricht und eine vom Benutzer bereitgestellte Funktion mit dieser Nachricht Nutzlast als Argument ausgeführt. Dies ist lediglich eine Meldung Ziel.
Zeitgeber <Type> Wird eine Nachricht an Ihr Ziel nach einer benutzerdefinierten Zeitspanne weitergegeben. Dies kann wiederholte nicht wiederholtes oder. Dieser Block ist lediglich eine Nachrichtenquelle.
Auswahl < Type1, Type2... > Nimmt die Nachrichten aus mehreren Quellen mit mehreren Typen und akzeptiert nur, dass die Nachricht von den ersten Block, die an die Wahl weitergegeben.
<Type> beitreten Nimmt Nachrichten aus mehreren Quellen und kombiniert diese zusammen in eine einzelne Nachricht ausgeben. Nachrichten werden aus jeder Quelleneingabe können wartet asynchron.
Multitype_join < Type1, Type2... > Nimmt Nachrichten aus mehreren Quellen mit mehreren Typen und kombiniert diese. Nachrichten werden aus jeder Quelleneingabe können wartet asynchron.

Einer der Hauptvorteile von Message Block primitive vom der AAL ist, dass Sie zusammenstellbar sind. Aus diesem Grund können Sie das gewünschte Verhalten Grundlage kombinieren. Beispielsweise können Sie problemlos einen Block erstellen, der mehrere Eingaben addiert durch Anfügen eines Blocks Transformator an das Ende einer Join-Block. Bei Join-Block Nachrichten von jedem seiner Quellen abrufen erfolgreich, können Sie diese der Transformator übergeben die Nachricht Nutzlasten summiert.

Sie könnten auch einen wiederholten Zeitgeber-Block als Quelle für eine Join-Block verbinden. Dies führt zu einem Block, der Nachrichten drosselt, nur über immer zu lassen den Zeitgeber-Block löst eine Meldung angezeigt. Diese zwei zusammenstellbar Blöcke werden in Abbildung 4 dargestellt.

image: Composing Adder and Message Throttling Blocks from Primitives

Abbildung 4 Verfassen Adder und Message-Drosselung Blöcke von Primitives

Erstellen einer Pipeline Message übergeben

Jetzt let’s werfen Sie einen Blick auf den Code beim Erstellen der Nachricht-Block-Pipeline weiter oben gezeigt. Wir können die Pipeline mit zwei Transformator Message Blocks ersetzen, wie in der Abbildung 5 . Ein Transformator Block dient eine Nachricht eines bestimmten Typs und führen Sie eine Benutzer-­-Funktion auf die Nachricht definiert die Nutzlast der Nachricht ändern oder sogar vollständig ändern Sie den Typ der Nachricht. Beispielsweise Censor-Block verwendet als Eingabe eine Nachricht, die eine Zeichenfolge enthält und es verarbeiten muss.

image: A Message Block Pipeline

Abbildung 5 A Message Block-Pipeline

Der Code zum Erstellen und verbinden die Nachricht blockiert wird in Abbildung 6 angezeigt. Dieser Code beginnt mit der Instanziierung der beiden Transformator Nachricht blockiert. Der C ++ 0 x Lambda-Parameter für den Censor Block-Konstruktor definiert die Transformationsfunktion sucht die Meldung gespeicherte Eingabezeichenfolge in ein Wörterbuch, das angezeigt wird, wenn es an eine andere Zeichenfolge geändert werden soll. Die resultierende Zeichenfolge wird zurückgegeben, und innerhalb des Blocks Censor hat dann in eine Nachricht eingeschlossen und außerhalb des Blocks weitergegeben. Ein ähnlicher Pfad wird für den Format-Transformator-Block ausgeführt, außer die Ausgabe eine Zeichenfolge ist, die von einer Funktion Format geändert wurde.

Abbildung 6 Simple Message-Pipeline

dictionary dict;
transformer<string, string> 
  censor([&dict](const string& s) -> string {
  string result = s;
  auto iter = dict.find(s);
  if (iter != dict.end()) {
    result =  iter->second;
  }
  return result;
});
transformer<string, string> 
  format([](const string& s) -> string {
  string result = s;
  for (string::size_type i = 0; i < s.size(); i++) {
    result[i] = (char)Format(s[i]);
  }
  return result;
});
censor.link_target(&format);
asend(&censor, "foo");
string newStr = receive(format);
printf("%s\n", newStr);

Nach der Instanziierung von zwei Blöcke verknüpft die nächste Zeile zwei Blöcke miteinander durch Aufrufen der Link_target-Methode des Blocks Censor. Jeder Block Quell- und Propagator hat eine Link_target-Methode, die verwendet wird, bestimmen die Meldung blockiert die Quelle seine Nachrichten verbreiten sollten.

Nachdem die Blöcke Censor und Format miteinander verknüpft wurden, alle Nachrichten in den Block Censor übertragen, durchlaufen die Transformationsfunktion und die resultierende Meldung wird implizit auf dem Block Format zur Verarbeitung übergeben werden. Wenn ein Block Nachricht eine Quelle ist oder Propagator noch keine verbundenen Ziele hat, kann der Message Block Informationsspeicher die Nachricht in einer Block-spezifische Weise bis ein Ziel verknüpft ist, oder die Nachricht wird abgerufen.

Die letzten drei Zeilen des Beispielcodes Anzeigen der Nachrichten in einem Block initiieren und Abrufen einer Nachricht aus einem Block. Es gibt zwei Nachricht Initiierung-APIs in der AAL: Sende- und Asend. Diese synchron und asynchron, jeweils eine Nachricht in einen Block eingeben.

Der Hauptunterschied besteht darin, dass bei ein senden-Aufruf zurückgegeben wird, es garantiert hat bereits abgelegt haben die Nachricht in und über den Block, den die Nachricht gesendet wird. Der Aufruf Asend kann sofort zurückgegeben und die Parallelität Runtime so planen Sie die Verteilung ermöglicht. Entsprechend sind zwei Nachrichtenübermittlung-APIs in der AAL: empfangen und Try_receive. Die Receive-Methode blockiert, bis eine Nachricht eintrifft, während die Try_receive sofort zurückgegeben wird, wenn es kann nicht zum Abrufen einer Nachricht ist.

Abbildung 6 wird die Zeichenfolge “ Foo ” asynchron an den Block Censor gesendet,. Censor-Block die Nachricht übernehmen, überprüfen, ob die Zeichenfolge im Wörterbuch Wörter censored ist und dann die resultierende Zeichenfolge in einer Nachricht übertragen. Dies wird dann an den Format-Block übergeben werden, wird die Zeichenfolge in Anspruch nehmen, Großbuchstaben für jeden Buchstaben und da keine Ziele hat die Nachricht enthalten. Beim Empfang wird aufgerufen, wird er die Nachricht aus dem Block Format abrufen. Auf diese Weise sähe vorausgesetzt, “ Foo ” wurde nicht im Wörterbuch, die Ausgabe dieses Beispiels “ FOO ”. Während dieses Beispiel nur eine einzige Zeichenfolge über das Netzwerk verschiebt, können Sie sehen, wie ein Stream von Eingabezeichenfolgen der Ausführung eine Pipeline bildet.

Betrachten in diesem Beispiel messaging, bemerken Sie die unterschiedliche fehlende Verweise auf die Nachrichten selbst. Eine Nachricht ist einfach einen Umschlag, der die Daten umbrochen wird, um Ihr Netzwerk Datenfluss übergeben werden sollen. Die Nachricht selbst erfolgt durch eine angeboten und akzeptiert. Wenn ein Block Nachricht eine Nachricht empfängt, kann er die Nachricht auf irgendeine Weise gespeichert werden sollen. Wenn Sie später eine Nachricht senden möchte, bietet es die Meldung an alle seine verbundenen Ziele. Um die Nachricht sofort tatsächlich nutzen zu können, muss der Empfänger die angebotene Nachricht zum Beenden der Transaktion akzeptieren. Dieser Gesamtprozess der Nachrichtenübermittlung zwischen Blöcken ist geplant und von Tasks, die geplant und ausgeführt, indem die Parallelität Runtime behandelt.

Übermittlung von Nachrichten blockieren

Nun, da Sie gesehen haben, wie Nachricht blockiert erstellt und miteinander verbunden werden und wie Nachrichten eingeleitet werden können, und eine kurze entnommen, die diese let’s nutzen betrachten werden wie Nachrichten geleitet, zwischen Blöcken und die Parallelität Runtime wie das Kernstück der AAL passt.

Diese Information ist nicht für die Verwendung von Nachricht blockiert, oder die AAL erforderlich, aber helfen, erhalten ein besseres Verständnis der Funktionsweise der Weitergabe von Nachrichten-Protokolle, und wie Sie Sie nutzen können. Für den Rest dieses Abschnitts bespreche ich Propagator-Blöcke da Sie beide Quellen und Ziele sind. Natürlich wäre eine reine Quelle oder einen Block von reinen Ziel einfach eine Teilmenge der Propagator Block-Implementierung.

Intern, hat jeder Block Propagator eine Eingangswarteschlange für Nachrichten und anderen Block-spezifischen Speichercontainer für Nachrichten. Andere Blöcke, die mit diesem Block Propagator Nachrichten, die in der Eingabewarteschlange gespeichert sind.

Abbildung 7 hat beispielsweise der Censor Transformator Block eine Eingangswarteschlange, die derzeit eine Nachricht mit einer Zeichenfolge str6 darin gespeichert wird. Aktuelle Transformator selbst enthält zwei Nachrichten: str4 und str5. Da dies ein Transformator ist, ist deren Speicherung Block-spezifischen eine andere Warteschlange. Andere Blocktypen können unterschiedliche Speichercontainer haben. Der Block Overwrite_buffer speichert beispielsweise nur eine einzelne Nachricht, die immer überschrieben werden würden.

image: Message-Passing Protocol

Abbildung 7 Message übergeben Protocol

Wenn eine Nachricht in einen Block aus einer der verknüpften Quellen (oder Sende-/Asend APIs) den Block zuerst angezeigt wird, prüft eine Filterfunktion zu bestimmen, ob die Nachricht akzeptiert. Wenn die Nachricht akzeptiert, wird die Nachricht in der Eingabewarteschlange eingefügt. Ein Filter ist eine optionale Funktion, die in den jedes Ziel-Konstruktor übergeben werden kann, oder Propagator-Block, der einen booleschen Wert, der bestimmt zurückgibt, ob eine Nachricht von einer Quelle angeboten werden akzeptiert. Wenn die Nachricht abgelehnt wird, wird die Quelle für das nächste Ziel bieten die Meldung fortgesetzt.

Sobald eine Nachricht in der Eingabewarteschlange eingefügt wird, behält der Quell-Block nicht mehr von stammt der Nachricht. Akzeptieren Block hat keine jedoch noch die Nachricht für die Übermittlung bereit. Auf diese Weise können Nachrichten nach oben in der Eingabewarteschlange Puffer während diese Verarbeitung warten.

Beim Eintreffen einer Nachricht in eine Eingangswarteschlange für einen Datenblock der Nachricht wird eine einfache Aufgabe (LWT) innerhalb der Parallelität Runtime Taskplaner geplant. Der Zweck dieses LWT ist hat zwei. Zunächst müssen Sie Nachrichten die Eingabewarteschlange in internen Speicher des Blocks verschieben (was wir als Nachrichtenverarbeitung zu finden). Zweitens müssen Sie auch versuchen, Weitergabe von Nachrichten an alle Ziele (was wir als Nachricht Weitergabe bezeichnet).

Beispielsweise in Abbildung 7 sind Nachrichten in der Eingabewarteschlange, die die LWT geplant werden aufgefordert. Die LWT verarbeitet die Nachricht anschließend durch die erste Ausführung der Transformator vom Benutzer bereitgestellte Funktion der Nachricht, das Zeichenfolgenwörterbuch censored Einchecken und anschließend Verschieben der Nachricht in den Puffer Speicher für den Block.

Nach der Übertragung in einen Puffer speichern, beginnt die LWT den Weitergabe Schritt, an die Nachrichten an den Ziel-Block Format gesendet werden. In diesem Fall da Nachricht str4 am Anfang des Transformators verwendet wurde, es wird übertragen, den Format-Block zuerst, und wird die nächste Nachricht str5, weitergegeben. Der gesamte denselben Prozess tritt auf der Format-Block.

Nachrichtenverarbeitung kann je nach Art des Blocks Nachricht abweichen. Beispielsweise hat ein Unbounded_buffer einen einfachen Verarbeitungsschritt Verschieben einer Nachricht in den Puffer speichern. Der Transformator verarbeitet Nachrichten durch den Aufruf der benutzerdefinierten Funktion für die Nachricht vor dem Verschieben in einen Puffer speichern. Andere Blöcke können noch komplexer, wie z. B. die Verknüpfung werden die Kombinieren mehrerer Nachrichten, die aus verschiedenen Quellen, und speichern Sie diese in einen Puffer in Vorbereitung der Verteilung müssen.

Aus Effizienzgründen der Leistung ist die AAL bei seiner Erstellung von LWTs intelligente so, dass nur eine für jeden Block Nachricht gleichzeitig geplant ist. Wenn weitere Nachrichten in eine Eingangswarteschlange eintreffen während die Verarbeitung LWT aktiv ist, wird es weiterhin abholen und verarbeitet diese Nachrichten. Folglich in Abbildung 7, wird Wenn der Transformator LWT weiterhin verarbeitet werden, wenn Nachricht str7 die Eingabewarteschlange eingibt abholen und starten eine neue Aufgabe Verarbeitung und Weitergabe, anstatt diese Nachricht verarbeitet.

Die Tatsache, dass jeder Block Nachricht eigene LWT verfügt, die Verarbeitung und Übertragung behandelt ist von zentraler Bedeutung für den Entwurf, der Arbeit in einer Weise Datenfluss pipeline für das Weitergabe von Nachrichten-Framework ermöglicht. Da jede Nachricht blockieren die Verarbeitung und Übertragung von Nachrichten in einem eigenen LWT durchführt, ist das AAL die Blöcke von einander zu entkoppeln und ermöglichen parallele Arbeit über mehrere Blöcke ausgeführt werden können. Jede LWT muss einfach seine Meldungen in die Ziel-Blöcke Eingabewarteschlangen weiterleiten, und jedes Ziel wird einfach ein LWT eigene Eingaben behandeln planen. Mit einer einzigen LWT verarbeiten und weitergeben wird sichergestellt, dass die Reihenfolge der Nachricht für die Nachricht blockiert verwaltet wird.

Asynchrone Agents

Die zweite Hauptkomponente von der AAL ist der asynchronen Agent. Asynchrone Agents sind grobe Anwendungskomponenten, die größere IT-Aufgaben und e/a-asynchron verarbeiten. Agents werden erwartet, zur Kommunikation mit anderen Agents und Parallelität auf unterer Ebene zu initiieren. Sie sind isoliert, da Ihre Ansicht der Welt ist vollständig in Ihrer Klasse enthalten, und Sie kommunizieren mit anderen Anwendungskomponenten, durch das Weiterleiten von Nachrichten verwenden. Agents selbst werden als in der Parallelität Aufgaben geplant. Dies ermöglicht es Ihnen, zu sperren und ergeben zusammen mit anderen arbeiten, die zur gleichen Zeit ausführen.

Ein asynchroner Agent hat einen Lebenszyklus festlegen wie in Abbildung 8 . Lebenszyklus der kann überwacht und auf gewartet werden. Bundesstaaten Grün hervorheben ausgeführten Zustände, während Status rot terminal Zustände sind. Entwickler können Ihre eigenen Agenten durch Ableitung von der Basis Agent-Klasse erstellen.

image: The Asynchronous Agent Lifecycle

Abbildung 8 die asynchrone Agent Lifecycle

Drei Funktionen Klasse basieren – starten, Abbrechen und – für den Übergang des Agents zwischen den verschiedenen Zuständen. Nachdem konstruiert, sind Agents im Zustand erstellt. Starten einen Agent ähnelt einen Thread gestartet. Sie werden alles, was nicht ausgeführt, bis die Start-Methode aufgerufen wird. Zu diesem Zeitpunkt der Agent zur Ausführung geplant ist, und der Agent in ausführbarer Zustand wechselt.

Einrichten der Agent die Parallelität Laufzeit auswählt, wird in der gestarteten Zustand verschoben und wird weiterhin ausgeführt, bis der Benutzer die done-Methode aufruft, der seine Arbeit abgeschlossen hat. Jederzeit nach der Agent geplant, aber noch nicht gestartet wurde, wurde ein Aufruf zum Abbrechen wird den Agent auf einen abgebrochenen Status übergehen und wird niemals ausgeführt.

Let’s uns wieder im e-Mail-Filterung wird, Datenfluss in der Anwendung eingeführt und eine verbesserte Fähigkeit zum parallelen Prozess Wörter, in denen die Pipeline Nachricht blockiert. Allerdings wurde im Beispiel nicht wie die e/A der Umgang mit den e-Mail-Nachrichten selbst und unterbrechen Sie in Streams von Zeichenfolgen für die Pipeline-Verarbeitung angezeigt. Auch nachdem die Zeichenfolgen durch die Pipeline übergeben wurden, müssen die Zeichenfolgen erfasst werden, damit der Text in dem Zustand neu censored und formatierte neu geschrieben werden kann. Dies ist die Agents kommen ins Spiel können damit die Unterschiede in der Wartezeiten mit e/A zu tolerieren.

Betrachten Sie beispielsweise am Ende unserer e-Mail-Pipeline. Zu diesem Zeitpunkt Zeichenfolgen werden durch das Format ausgegeben wird und auf Dateien in einem Postfach geschrieben werden müssen. Abbildung 9 zeigt, wie ein Ausgabe-Agent Zeichenfolgen zu erfassen und Ausgabe von e-Mail-Nachrichten erstellen kann. Die run-Funktion die WriterAgent empfängt Nachrichten aus dem Format-Block in einer Schleife.

image: An Agent Capturing the Output of the Format Block

Abbildung 9 eine Aufnahme der Ausgabe des Format-Block Agent

Während der Großteil der Verarbeitung erfolgt in dieser Anwendung Datenfluss verwendet, zeigt der WriterAgent wie einige Ablaufsteuerung in das Programm integriert werden kann. Beispielsweise beim Eintreffen einer Nachricht das Ende der Datei muss der WriterAgent unterschiedliches Verhalten abhängig von der Eingabezeichenfolge empfangen haben, es muss wissen, um den Vorgang beendet. Abbildung 10 wird der Code für die WriterAgent.

Abbildung 10 WriterAgent

class WriterAgent : public agent {
public:
  WriterAgent(ISource<string> * src) : m_source(src) {
  }
  ~WriterAgent() {
    agent::wait(this);
  }
  virtual void run() {
    FILE *stream;
    fopen_s( &stream, ... );
    string s;
    string eof("EOF");
    while (!feof(stream) && ((s=receive(m_source)) != eof)) {
      write_string(stream, s);
    }
    fclose(stream);
    done();
  }
private:
  ISource<string> * m_source;
};

Es gibt ein paar interessante Teile dieser Code zu beachten. Erstens wird innerhalb der Destruktor eine statische Funktion agent::wait aufgerufen. Diese Funktion kann mit einem Zeiger auf einem beliebigen Agent aufgerufen werden und wird blockiert, bis der Agent einen Terminalserver Status eintritt: abgeschlossen oder abgebrochen wurde. Aufrufende Wartezeit im Destruktor ist, zwar nicht erforderlich, für alle Agents sollten in den meisten Fällen es möglich, wie es stellt sicher, der Agent nicht mehr Code ausgeführt wird dass, wenn destructing.

Zweitens ist ein interessanter Aspekt dieses Codes die run-Methode selbst. Diese Methode definiert die wichtigste Ausführung des Agents. In diesem Code wird der Agent Umgang mit Zeichenfolgenset von der Quelle (in unserem Beispiel den Format-Block) schreiben.

Beachten Sie schließlich, die letzte Zeile der run-Methode, die ein Aufruf an die Agent-Funktion ausgeführt wird. Der Aufruf an die done-Methode verschiebt den Agent aus der laufenden Zustand in den Status erledigt. In den meisten Fällen benötigen diese am Ende der run-Methode aufgerufen werden. Jedoch unter bestimmten Umständen sollten Anwendungen mit Agents Zustand, wie z. B. in einem Datenfluss-Netzwerk anzugeben, die über die Lebensdauer der run-Methode aktiv bleiben sollen.

Blockieren alles zusammen

Jetzt, da wir eine messaging-Pipeline zum Filtern und Formatieren von Zeichenfolgen und ein Agent Ausgabe verarbeiten erstellt haben, können wir einen Eingaben-Agent hinzufügen, der sehr ähnliches Verhalten an den Ausgabe-Agent verfügt. Abbildung 11 zeigt ein Beispiel für diese Anwendung zusammen wie passt.

image: Agents Used to Process E-mail Messages

Abbildung 11 Agents wird verwendet, um e-Mail-Nachrichten von Process

Einer der Vorteile bei der Verarbeitung von Agents ist die Möglichkeit, asynchrone Akteure in der Anwendung verwenden. Auf diese Weise bei Empfang der Daten für die Verarbeitung startet der input-Agent asynchron senden die Zeichenfolgen durch die Pipeline und die Ausgabe-Agent gelesen und Ausgabedateien ebenso können. Diese Akteure können starten und Stoppen der Verarbeitung völlig unabhängig voneinander und vollständig gesteuert, indem Daten. Ein solches Verhalten funktioniert hervorragend in vielen Szenarios, die Wartezeit besonders gesteuert und asynchrone e/A, wie im Beispiel e-Mail-Verarbeitung.

In diesem Beispiel hinzugefügt habe ich einen zweiten Agent eine ReaderAgent, welche Handlungen ebenso auf die WriterAgent außer es verarbeitet die e/A für den Umgang mit die e-Mail-Nachrichten lesen und Senden von Zeichenfolgen an das Netzwerk. Abbildung 12 wird der Code für die ReaderAgent.

Abbildung 12 ReaderAgent

class ReaderAgent : public agent {
public:
  ReaderAgent(ITarget<string> * target) : m_target(target) {
  }
  ~ReaderAgent() {
    agent::wait(this);
  }
  virtual void run() {
    FILE *stream;       
    fopen_s( &stream, ...);
    while (!feof(stream)) {
      asend(m_target, read_word(stream));
    }
    fclose( stream );
    asend(m_target, string("eof"));
    done();
  }
private:
  ITarget<string> * m_target;
};

Jetzt, da wir ein ReaderAgent und eine WriterAgent die e/A für das Programm asynchron verarbeitet haben, müssen wir einfach bis zu den Transformator Blöcke im Netzwerk mit der Verarbeitung beginnen zu verknüpfen. Dies kann problemlos nach dem Verknüpfen von zwei Blöcke gemeinsam erfolgen:

censor.link_target(&format);
ReaderAgent r(&censor);
r.start();
WriterAgent w(&format);
w.start();

Die ReaderAgent wird mit einem Verweis auf die Censor erstellt, sodass es einwandfrei Nachrichten senden kann, während die WriterAgent mit einem Verweis auf das Format erstellt wird, damit Nachrichten abgerufen werden können. Jeder Agent ist mit seinen Start-API, Beginn, den Agents für die Ausführung in der Parallelität plant. Da jeder Agent die agent::wait(this) in einem eigenen Destruktor aufgerufen wird, wird die Ausführung warten, bis beide Agents done Status erreicht haben.

Synchronisieren von

In diesem Artikel wurde geschrieben, um Sie geben einen Einblick in einige neuen Möglichkeiten für Akteur basierende Programmierung und Datenfluss pipelining in Visual Studio 2010 integriert. Wir empfehlen Ihnen, um es auszuprobieren.

Wenn Sie tiefer untersuchen möchten, stehen zahlreiche weitere Funktionen, die wir in diesem Artikel behandelt wurden nicht: Benutzerdefinierte Meldung Block Erstellung Filtern von Nachrichten und vieles mehr. Parallel Computing Developer Center auf MSDN (msdn.microsoft.com/concurrency ) enthält weitere Details und exemplarischen Vorgehensweisen, die von wie diesem aufregende neue Programmiermodell Sie Ihr Programm auf völlig neue Weise parallelisieren helfen kann.

Michael Chu ist eine Entwicklungs-Softwareentwickler im Parallel Computing Platform-Gruppe bei Microsoft. Er arbeitet an Parallelität Runtime-Team.

Krishnan Varadarajan ist eine Entwicklungs-Softwareentwickler im Parallel Computing Platform-Gruppe bei Microsoft. Er arbeitet an Parallelität Runtime-Team.

Dank an die folgenden technischen Experten für die Überprüfung der in diesem Artikel: Parallelität Runtime-team