Libreria di agenti asincroni
La libreria di agenti asincroni, o semplicemente libreria di agenti, fornisce un modello di programmazione che consente di migliorare l'affidabilità dello sviluppo di applicazioni basate sulla concorrenza. La libreria di agenti è una libreria di modelli di C++ che promuove un modello di programmazione basato su attori e un passaggio dei messaggi in-process per le attività di pipelining o per un flusso di dati meno accurato. La libreria di agenti si basa sulla pianificazione e sui componenti di gestione delle risorse del runtime di concorrenza.
Modello di programmazione
La libreria di agenti offre alternative allo stato condiviso consentendo la connessione di componenti isolati attraverso un modello di comunicazione asincrona basato sul flusso di dati anziché sul flusso di controllo. Il flusso di dati si riferisce a un modello di programmazione in cui i calcoli vengono eseguiti quando sono disponibili tutti i dati necessari; 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 di agenti è composta da tre componenti: agenti asincroni, blocchi dei messaggi asincroni e funzioni di passaggio dei messaggi. Gli agenti mantengono lo stato e utilizzano i blocchi dei messaggi e le funzioni di passaggio dei messaggi per comunicare tra loro e con i componenti esterni. Le funzioni di passaggio dei messaggi consentono agli agenti di inviare e ricevere messaggi da componenti esterni. I blocchi dei messaggi asincroni contengono i messaggi e consentono agli agenti di comunicare in modo sincronizzato.
L'illustrazione seguente mostra come due agenti utilizzano blocchi dei messaggi e le funzioni di passaggio dei messaggi per comunicare. In questa illustrazione agent1 invia un messaggio a agent2 tramite la funzione Concurrency::send e un oggetto Concurrency::unbounded_buffer. agent2 utilizza la funzione Concurrency::receive per leggere il messaggio. agent2 utilizza lo stesso metodo per inviare un messaggio a agent1. Le frecce tratteggiate rappresentano il flusso di dati tra gli agenti. Le frecce continue connettono gli agenti ai blocchi dei messaggi in cui scrivono o da cui leggono.
Un esempio di codice che implementa questa illustrazione viene mostrato più avanti in questo argomento.
Il modello di programmazione dell'agente offre diversi vantaggi rispetto agli altri meccanismi di concorrenza e sincronizzazione, ad esempio gli eventi. Un vantaggio è costituito dal fatto che utilizzando il passaggio dei messaggi per trasmettere le modifiche di stato tra gli oggetti, è possibile isolare l'accesso alle risorse condivise migliorando pertanto la scalabilità. Uno dei vantaggi che derivano dal passaggio dei messaggi risiede nel fatto che la sincronizzazione è collegata ai dati anziché a un oggetto di sincronizzazione esterno. In questo modo viene semplificata la trasmissione dei dati tra componenti eliminando gli errori di programmazione nelle applicazioni.
Quando utilizzare la libreria di agenti
Utilizzare la libreria di agenti quando si dispone di più operazioni che devono comunicare tra loro in modo asincrono. I blocchi dei messaggi e le funzioni di passaggio dei messaggi consentono di scrivere applicazioni parallele senza la necessità dei meccanismi di sincronizzazione come i blocchi, consentendo di concentrarsi sulla logica dell'applicazione.
Il modello di programmazione dell'agente viene spesso utilizzato per creare pipeline di dati o reti. Una pipeline di dati è costituita da una serie di componenti, ognuno dei quali esegue un'attività specifica che contribuisce a un obiettivo più grande. Ogni componente di una pipeline del flusso di dati esegue un lavoro quando riceve un messaggio da un altro componente. Il risultato di tale lavoro viene passato agli altri componenti della pipeline o della rete. I componenti possono utilizzare le funzionalità di concorrenza più accurate presenti nelle altre librerie, ad esempio PPL (Parallel Patterns Library).
Esempio
Nell'esempio seguente viene implementata l'illustrazione mostrata 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);
}
Questo esempio produce l'output seguente:
agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.
Negli argomenti seguenti viene descritta la funzionalità utilizzata in questo esempio.
Argomenti correlati
Agenti asincroni
Viene descritto il ruolo degli agenti asincroni nella risoluzione delle attività di elaborazione più grandi.Blocchi dei messaggi asincroni
Vengono descritti i vari tipi di blocco dei messaggi forniti dalla libreria di agenti.Funzioni di passaggio dei messaggi
Vengono descritte le varie routine di passaggio dei messaggi fornite dalla libreria di agenti.Procedura: implementare vari modelli producer-consumer
Descrive come implementare il modello producer-consumer nell'applicazione.Procedura: fornire funzioni lavoro alle classi call e transformer
Vengono illustrati i vari modi per fornire le funzioni lavoro alle classi Concurrency::call e Concurrency::transformer.Procedura: Utilizzare la classe transformer in una pipeline di dati
Viene mostrato come utilizzare la classe Concurrency::transformer in una pipeline di dati.Procedura: effettuare una scelta tra le attività completate
Viene mostrato come utilizzare 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
Viene mostrato come utilizzare la classe Concurrency::timer per inviare un messaggio a intervalli regolari.Procedura: utilizzare il filtro di blocco dei messaggi
Viene illustrato come utilizzare un filtro per consentire a un blocco di messaggi asincroni di accettare o rifiutare i messaggi.PPL (Parallel Patterns Library)
Viene descritto come utilizzare i vari modelli paralleli, ad esempio gli algoritmi paralleli, nelle applicazioni.Runtime di concorrenza
Viene descritto il runtime di concorrenza che semplifica la programmazione parallela e vengono forniti i collegamenti ad argomenti correlati.