Biblioteki agentów asynchronicznych
Biblioteka agentów asynchronicznych (lub po prostu biblioteka agentów) udostępnia model programowania, który umożliwia zwiększenie niezawodności tworzenia aplikacji z obsługą współbieżności. Biblioteka agentów to biblioteka szablonów języka C++, która promuje model programowania oparty na aktorach i przekazywanie komunikatów w procesie dla grubszego przepływu danych i zadań potokowych. Biblioteka agentów bazuje na składnikach planowania i zarządzania zasobami środowiska uruchomieniowego współbieżności.
Model programowania
Biblioteka agentów udostępnia alternatywy dla stanu udostępnionego, umożliwiając łączenie izolowanych składników za pośrednictwem modelu komunikacji asynchronicznej opartego na przepływie danych zamiast przepływu sterowania. Przepływ danych odnosi się do modelu programowania, w którym obliczenia są wykonywane, gdy wszystkie wymagane dane są dostępne; Przepływ sterowania odnosi się do modelu programowania, w którym obliczenia są wykonywane w wstępnie określonej kolejności.
Model programowania przepływu danych jest związany z koncepcją przekazywania komunikatów, gdzie niezależne składniki programu komunikują się ze sobą przez wysyłanie komunikatów.
Biblioteka agentów składa się z trzech składników: asynchronicznych agentów, asynchronicznych bloków komunikatów i funkcji przekazywania komunikatów. Agenci utrzymują stan i używają bloków komunikatów i funkcji przekazywania komunikatów, aby komunikować się ze sobą i ze składnikami zewnętrznymi. Funkcje przekazywania komunikatów umożliwiają agentom wysyłanie i odbieranie komunikatów do i z składników zewnętrznych. Komunikat asynchroniczny blokuje blokowanie komunikatów i umożliwia agentom komunikowanie się w sposób zsynchronizowany.
Na poniższej ilustracji pokazano, jak dwaj agenci używają bloków komunikatów i funkcji przekazywania komunikatów do komunikacji. Na tej ilustracji agent1
wysyła komunikat agent2
za pomocą funkcji concurrency::send i obiektu concurrency::unbounded_buffer . agent2
używa funkcji concurrency::receive, aby odczytać komunikat. agent2
używa tej samej metody, aby wysłać komunikat do agent1
. Strzałki kreskowane reprezentują przepływ danych między agentami. Stałe strzałki łączą agentów z blokami komunikatów, z których zapisują się lub odczytują.
Przykładowy kod, który implementuje tę ilustrację, jest pokazany w dalszej części tego tematu.
Model programowania agentów ma kilka zalet w porównaniu z innymi mechanizmami współbieżności i synchronizacji, na przykład zdarzeniami. Jedną z zalet jest użycie przekazywania komunikatów w celu przesyłania zmian stanu między obiektami, można odizolować dostęp do zasobów udostępnionych, a tym samym zwiększyć skalowalność. Zaletą przekazywania komunikatów jest powiązanie synchronizacji z danymi zamiast wiązania ich z obiektem synchronizacji zewnętrznej. Upraszcza to przesyłanie danych między składnikami i może wyeliminować błędy programowania w aplikacjach.
Kiedy należy używać biblioteki agentów
Użyj biblioteki Agents, jeśli masz wiele operacji, które muszą komunikować się ze sobą asynchronicznie. Bloki komunikatów i funkcje przekazywania komunikatów umożliwiają pisanie aplikacji równoległych bez konieczności stosowania mechanizmów synchronizacji, takich jak blokady. Dzięki temu można skupić się na logice aplikacji.
Model programowania agenta jest często używany do tworzenia potoków danych lub sieci. Potok danych to seria składników, z których każdy wykonuje określone zadanie, które przyczynia się do większego celu. Każdy składnik w potoku przepływu danych wykonuje pracę, gdy odbiera komunikat z innego składnika. Wynik tej pracy jest przekazywany do innych składników w potoku lub sieci. Składniki mogą używać bardziej szczegółowej funkcji współbieżności z innych bibliotek, na przykład biblioteki równoległych wzorców (PPL).
Przykład
Poniższy przykład implementuje ilustrację pokazaną wcześniej w tym temacie.
// 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);
}
Ten przykład generuje następujące wyniki:
agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.
W poniższych tematach opisano funkcje używane w tym przykładzie.
Tematy pokrewne
Agenci asynchroniczni
Opisuje rolę agentów asynchronicznych w rozwiązywaniu większych zadań obliczeniowych.
Bloki komunikatów asynchronicznych
Opisuje różne typy bloków komunikatów udostępniane przez bibliotekę agentów.
Funkcje przekazywania komunikatów
Opisuje różne procedury przekazywania komunikatów udostępniane przez bibliotekę agentów.
Instrukcje: implementowanie różnych wzorców producent — konsument
Opisuje sposób implementowania wzorca producenta-konsumenta w aplikacji.
Instrukcje: zapewnianie funkcji pracy dla wywoływania oraz klasy transformatora
Ilustruje kilka sposobów zapewniania funkcji roboczych klas współbieżności::call i concurrency::transformer .
Instrukcje: używanie transformatora w potoku danych
Pokazuje, jak używać klasy concurrency::transformer w potoku danych.
Instrukcje: wybieranie spośród zadań wykonanych
Pokazuje, jak używać klas concurrency::choice i concurrency::join , aby wybrać pierwsze zadanie do ukończenia algorytmu wyszukiwania.
Instrukcje: wysyłanie komunikatu w regularnych odstępach czasu
Pokazuje, jak używać klasy concurrency::timer do wysyłania komunikatu w regularnych odstępach czasu.
Instrukcje: korzystanie z filtra bloku komunikatów
Przedstawia sposób użycia filtru w celu włączenia asynchronicznego bloku komunikatów w celu akceptowania lub odrzucania komunikatów.
Biblioteka równoległych wzorców (PLL)
Opisuje sposób używania różnych wzorców równoległych, takich jak algorytmy równoległe, w aplikacjach.
Środowisko uruchomieniowe współbieżności
Opisuje środowisko uruchomieniowe współbieżności, które upraszcza programowanie równoległe i zawiera linki do powiązanych tematów.