Asynchronous Agents Library

Die asynchrone Agents-Bibliothek (oder nur die Agentsbibliothek) bietet ein Programmiermodell, mit dem Sie die Stabilität der Parallelitäts-fähigen Anwendungsentwicklung erhöhen können. Die Agents Library ist eine C++-Vorlagenbibliothek, die ein akteurbasiertes Programmiermodell und prozessinterne Nachrichtenübergabe für undifferenzierten Datenfluss und Pipelineaufgaben begünstigt. Die Agents Library baut auf der Planung und den Ressourcenverwaltungskomponenten von der Concurrency Runtime auf.

Programmiermodell

Die Agents Library bietet Alternativen zum Freigabezustand, indem Sie isolierte Komponenten durch ein asynchrones Kommunikationsmodell verbinden können, das auf Datenfluss anstatt auf Ablaufsteuerung basiert. Dataflow bezieht sich auf ein Programmiermodell, bei dem Berechnungen vorgenommen werden, wenn alle erforderlichen Daten verfügbar sind; Der Steuerungsfluss bezieht sich auf ein Programmiermodell, bei dem Berechnungen in einer vordefinierten Reihenfolge erfolgen.

Das Datenflussprogrammiermodell bezieht sich auf das Konzept der Nachrichtenübergabe, bei dem unabhängige Komponenten eines Programms miteinander kommunizieren, indem sie Nachrichten senden.

Die Agents-Bibliothek besteht aus drei Komponenten: asynchronen Agents, asynchronen Nachrichtenblöcken und Nachrichtenübergabefunktionen. Agents behalten den Zustand bei und verwenden Meldungsblöcke und Meldungsübergabefunktionen für die Kommunikation miteinander und mit externen Komponenten. Mit Meldungsübergabefunktionen können Agents Meldungen an die externen Komponenten senden und von diesen erhalten. Asynchrone Meldungsblöcke halten Meldungen und ermöglichen es Agents, auf synchronisierte Weise zu kommunizieren.

Aus folgender Abbildung geht hervor, wie zwei Agents mithilfe von Meldungsblöcke und Meldungsübergabefunktionen kommunizieren. In dieser Abbildung agent1 sendet eine Nachricht agent2 mithilfe der Funktion "concurrency::send " und eines "concurrency::unbounded_buffer "-Objekts an. agent2 verwendet die Parallelitätsfunktion::receive , um die Nachricht zu lesen. agent2 verwendet dieselbe Methode, um eine Meldung an agent1 zu senden. Gestrichelte Pfeile stellen den Datenstrom zwischen Agents dar. Ausgefüllte Pfeile verbinden die Agents mit den Meldungsblöcken, auf die sie schreiben oder von denen sie lesen.

The components of the Agents Library.

Ein Codebeispiel, bei dem diese Abbildung implementiert wird, finden Sie weiter unten in diesem Thema.

Das Agent-Programmiermodell hat im Vergleich zu anderen Parallelitäts- und Synchronisierungsmechanismen verschiedene Vorteile wie Ereignisse. Ein Vorteil besteht darin, dass Sie durch die Verwendung der Meldungsübergabe zum Senden von Zustandsänderungen zwischen Objekten den Zugriff auf freigegebene Ressourcen isolieren und dadurch die Skalierbarkeit steigern können. Ein Vorteil der Meldungsübergabe ist, dass es die Synchronisierung an Daten bindet anstatt an ein externes Synchronisierungsobjekt. Dies vereinfacht die Datenübermittlung zwischen Komponenten und kann in den Anwendungen Programmierfehler ausschließen.

Wann die Agents Library verwendet werden soll

Verwenden Sie die Agents Library, wenn Sie mehrere Operationen haben, die asynchron miteinander kommunizieren müssen. Durch Meldungsblöcke und Meldungsübergabefunktionen können Sie parallele Anwendungen schreiben, ohne dass dabei Synchronisierungsmechanismen wie Sperren erforderlich sind. So können Sie sich auf die Anwendungslogik konzentrieren.

Das Agent-Programmiermodell wird häufig verwendet, um Datenpipelines oder Netzwerke zu erstellen. Eine Datenpipeline besteht aus einer Reihe von Komponenten, von denen jedes eine bestimmte Aufgabe ausführt, die zu einem größeren Ziel beiträgt. Jede einzelne Komponente in einer Datenflusspipeline führt Arbeiten aus, wenn sie von einer anderen Komponente eine Meldung empfängt. Das Ergebnis dieser Arbeit wird an andere Komponenten in der Pipeline oder im Netzwerk übergeben. Die Komponenten können präzisere Parallelitätsfunktionen aus anderen Bibliotheken verwenden, z. B. die Parallel Patterns Library (PPL).

Beispiel

Im folgenden Beispiel wird die Abbildung implementiert, die in diesem Thema bereits angezeigt wurde.

// basic-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// This agent writes a string to its target and reads an integer
// from its source.
class agent1 : public agent 
{
public:
   explicit agent1(ISource<int>& source, ITarget<wstring>& target)
      : _source(source)
      , _target(target)
   {
   }

protected:
   void run()
   {
      // Send the request.
      wstringstream ss;
      ss << L"agent1: sending request..." << endl;
      wcout << ss.str();

      send(_target, wstring(L"request"));

      // Read the response.
      int response = receive(_source);

      ss = wstringstream();
      ss << L"agent1: received '" << response << L"'." << endl;
      wcout << ss.str();

      // Move the agent to the finished state.
      done();
   }

private:   
   ISource<int>& _source;
   ITarget<wstring>& _target;
};

// This agent reads a string to its source and then writes an integer
// to its target.
class agent2 : public agent 
{
public:
   explicit agent2(ISource<wstring>& source, ITarget<int>& target)
      : _source(source)
      , _target(target)
   {
   }

protected:
   void run()
   {
      // Read the request.
      wstring request = receive(_source);

      wstringstream ss;
      ss << L"agent2: received '" << request << L"'." << endl;
      wcout << ss.str();

      // Send the response.
      ss = wstringstream();
      ss << L"agent2: sending response..." << endl;
      wcout << ss.str();

      send(_target, 42);

      // Move the agent to the finished state.
      done();
   }

private:   
   ISource<wstring>& _source;
   ITarget<int>& _target;
};

int wmain()
{
   // Step 1: Create two message buffers to serve as communication channels
   // between the agents.
   
   // The first agent writes messages to this buffer; the second
   // agents reads messages from this buffer.
   unbounded_buffer<wstring> buffer1;

   // The first agent reads messages from this buffer; the second
   // agents writes messages to this buffer.
   overwrite_buffer<int> buffer2;

   // Step 2: Create the agents.
   agent1 first_agent(buffer2, buffer1);
   agent2 second_agent(buffer1, buffer2);

   // Step 3: Start the agents. The runtime calls the run method on
   // each agent.
   first_agent.start();
   second_agent.start();

   // Step 4: Wait for both agents to finish.
   agent::wait(&first_agent);
   agent::wait(&second_agent);
}

Dieses Beispiel erzeugt die folgende Ausgabe:

agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.

In den folgenden Themen wird die in diesem Beispiel verwendete Funktionalität beschrieben.

Asynchrone Agents
Beschreibt die Rolle asynchroner Agents beim Lösen größerer Computeraufgaben.

Asynchrone Nachrichtenblöcke
Beschreibt die verschiedenen Meldungsblocktypen, die von der Agents Library bereitgestellt werden.

Funktionen zum Übergeben von Nachrichten
Beschreibt die verschiedenen Meldungsübergaberoutinen, die von der Agents Library bereitgestellt werden.

Vorgehensweise: Implementieren verschiedener Producer-Consumer-Muster
Beschreibt die Implementierung des Producer-Consumer-Musters in der Anwendung.

Vorgehensweise: Bereitstellen von Arbeitsfunktionen für die call- und transformer-Klassen
Veranschaulicht mehrere Möglichkeiten zum Bereitstellen von Arbeitsfunktionen für die Parallelitätsklassen::call und concurrency::transformer classes.

Vorgehensweise: Verwenden von transformer in einer Datenpipeline
Zeigt, wie die Parallelitätsklasse::transformator in einer Datenpipeline verwendet wird.

Vorgehensweise: Auswählen von abgeschlossenen Aufgaben
Zeigt, wie Sie die Parallelität::Choice und parallele Klassen verwenden , um die erste Aufgabe auszuwählen, um einen Suchalgorithmus abzuschließen.

Vorgehensweise: Senden einer Nachricht in regelmäßigen Intervallen
Zeigt, wie Sie die Parallelitätsklasse::timer verwenden, um eine Nachricht in einem normalen Intervall zu senden.

Vorgehensweise: Verwenden eines Nachrichtenblockfilters
Veranschaulicht die Verwendung eines Filters, um einem asynchronen Nachrichtenblock das Annehmen oder Ablehnen von Nachrichten zu ermöglichen.

Parallel Patterns Library (PPL)
Beschreibt, wie verschiedene parallele Muster wie parallele Algorithmen in den Anwendungen verwendet werden.

Concurrency Runtime
Beschreibt die Concurrency Runtime, die die parallele Programmierung vereinfacht, und stellt Links zu verwandten Themen bereit.