Share via


Libreria di agenti asincroni

La libreria degli agenti asincroni (o solo libreria agenti) fornisce un modello di programmazione che consente di aumentare l'affidabilità dello sviluppo di applicazioni abilitate per la concorrenza. La libreria agenti è una libreria di modelli C++ che promuove un modello di programmazione basato su attore e il passaggio di messaggi in-process per attività di pipelining e flusso di dati con granularità grossolana. La libreria agenti si basa sui componenti di pianificazione e gestione delle risorse del runtime di concorrenza.

Modello di programmazione

La libreria agenti offre alternative allo stato condiviso consentendo di connettere componenti isolati tramite un modello di comunicazione asincrono basato sul flusso di dati anziché sul flusso di controllo. Il flusso di dati fa riferimento a un modello di programmazione in cui vengono eseguiti i calcoli quando tutti i dati necessari sono disponibili; il flusso di controllo si riferisce a un modello di programmazione in cui i calcoli vengono eseguiti in un ordine predeterminato.

Il modello di programmazione del flusso di dati è correlato al concetto di passaggio dei messaggi, in base al quale i componenti indipendenti di un programma comunicano con un altro programma inviando messaggi.

La libreria agenti è costituita da tre componenti: agenti asincroni, blocchi di messaggi asincroni e funzioni di passaggio dei messaggi. Gli agenti mantengono lo stato e usano blocchi di messaggi e funzioni di passaggio dei messaggi per comunicare tra loro e con componenti esterni. Le funzioni di passaggio dei messaggi consentono agli agenti di inviare e ricevere messaggi da e verso i componenti esterni. I blocchi di messaggi asincroni contengono messaggi e consentono agli agenti di comunicare in modo sincronizzato.

La figura seguente mostra come due agenti usano blocchi di messaggi e funzioni di passaggio dei messaggi per comunicare. In questa illustrazione, agent1 invia un messaggio a agent2 usando la funzione concurrency::send e un oggetto concurrency::unbounded_buffer . agent2 usa la funzione concurrency::receive per leggere il messaggio. agent2 usa lo stesso metodo per inviare un messaggio a agent1. Le frecce tratteggiate rappresentano il flusso di dati tra gli agenti. Le frecce a tinta unita connettono gli agenti ai blocchi di messaggi da cui scrivono o leggono.

The components of the Agents Library.

Un esempio di codice che implementa questa illustrazione viene illustrato più avanti in questo argomento.

Il modello di programmazione agente presenta diversi vantaggi rispetto ad altri meccanismi di concorrenza e sincronizzazione, ad esempio eventi. Un vantaggio è che l'uso del passaggio di messaggi per trasmettere le modifiche dello stato tra gli oggetti, è possibile isolare l'accesso alle risorse condivise e migliorare così la scalabilità. Un vantaggio del passaggio di messaggi è che collega la sincronizzazione ai dati anziché collegarla a un oggetto di sincronizzazione esterno. Ciò semplifica la trasmissione dei dati tra i componenti e può eliminare gli errori di programmazione nelle applicazioni.

Quando usare la libreria agenti

Usare la libreria Agents quando si dispone di più operazioni che devono comunicare tra loro in modo asincrono. I blocchi di messaggi e le funzioni di passaggio dei messaggi consentono di scrivere applicazioni parallele senza richiedere meccanismi di sincronizzazione come i blocchi. In questo modo è possibile concentrarsi sulla logica dell'applicazione.

Il modello di programmazione dell'agente viene spesso usato per creare pipeline o reti di dati. Una pipeline di dati è una serie di componenti, ognuno dei quali esegue un'attività specifica che contribuisce a un obiettivo più ampio. Ogni componente in una pipeline del flusso di dati esegue il lavoro quando riceve un messaggio da un altro componente. Il risultato di tale lavoro viene passato ad altri componenti nella pipeline o nella rete. I componenti possono usare funzionalità di concorrenza con granularità più fine di altre librerie, ad esempio la libreria PPL (Parallel Patterns Library).

Esempio

Nell'esempio seguente viene implementata l'illustrazione illustrata in precedenza in questo argomento.

// 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);
}

Nell'esempio viene prodotto l'output seguente:

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

Negli argomenti seguenti vengono descritte le funzionalità usate in questo esempio.

Agenti asincroni
Descrive il ruolo degli agenti asincroni nella risoluzione di attività di elaborazione più grandi.

Blocchi dei messaggi asincroni
Descrive i vari tipi di blocchi di messaggi forniti dalla libreria agenti.

Funzioni di passaggio dei messaggi
Descrive le varie routine di passaggio dei messaggi fornite dalla libreria agenti.

Procedura: Implementare vari modelli producer-consumer
Viene descritto come implementare il modello producer-consumer nell'applicazione.

Procedura: Fornire funzioni lavoro alle classi call e transformer
Illustra diversi modi per fornire funzioni di lavoro alle classi concurrency::call e concurrency::transformer .

Procedura: Usare la classe transformer in una pipeline di dati
Illustra come usare la classe concurrency::transformer in una pipeline di dati.

Procedura: Effettuare una scelta tra le attività completate
Illustra come usare le classi concurrency::choice e concurrency::join per selezionare la prima attività per completare un algoritmo di ricerca.

Procedura: Inviare un messaggio a intervalli regolari
Illustra come usare la classe concurrency::timer per inviare un messaggio a intervalli regolari.

Procedura: Usare il filtro di blocco dei messaggi
Illustra come usare un filtro per consentire a un blocco di messaggi asincrono di accettare o rifiutare i messaggi.

PPL (Parallel Patterns Library)
Viene descritto come usare vari modelli paralleli, ad esempio algoritmi paralleli, nelle applicazioni.

Runtime di concorrenza
Descrive il runtime di concorrenza che semplifica la programmazione parallela e contiene i collegamenti ad argomenti correlati.