异步代理库
异步代理库(或简称为“代理库”)提供了一个编程模型,通过它可以增加启用并发的应用程序开发的可靠性。 代理库是一个 C++ 模板库,它可以提升基于参与者的编程模型和进程内消息传递,以执行细化数据流任务和流水线操作任务。 代理库基于并发运行时的计划和资源管理组件生成。
编程模型
代理库可提供共享状态的备选方案,方法是让您通过基于数据流(而不是控制流)的异步通信模型来连接独立的组件。 “数据流”指的是在所有必需的数据都可用时进行计算的编程模型;“控件流”指的是按预先确定的顺序进行计算的编程模型。
数据流编程模型与“消息传递”概念相关,在消息传递中,某个程序的独立组件通过发送消息与另一个程序进行通信。
代理库由三个组件组成:“异步代理”、“异步消息块”和“消息传递函数”。 代理维护状态,并使用消息块和消息传递函数与另一个组件和外部组件进行通信。 通过消息传递函数,代理可以将消息发送到外部组件和从外部组件接收消息。 异步消息块存放消息,并使通信能够以同步的方式进行通信。
下图显示了两个代理如何使用消息块和消息传递函数进行通信。 在此图中, agent1会发送一条消息, agent2使用 concurrency::send 函数和 concurrency::unbounded_buffer 对象。 agent2使用 concurrency::receive 函数即可阅读邮件。 agent2 使用相同的方法向 agent1 发送消息。 虚线箭头表示代理之间的数据流。 实线箭头将代理连接到它们在其中写入或读取的消息块。
本主题后面将显示实现此图的代码示例。
代理编程模型具有其他并发和同步机制(如事件)所没有的一些优点。 一个优点是:通过使用消息传递在对象之间传输状态更改,您可以隔离对共享资源的访问,从而提高可伸缩性。 消息传递的优点是它将同步绑定到数据,而不是绑定到外部的同步对象。 这简化了组件之间的数据传输,并可以消除应用程序中的编程错误。
何时使用代理库
当您有多个必须彼此异步通信的操作时,使用代理库。 使用消息块和消息传递函数,无需同步机制(如锁)即可编写并行应用程序。 这使您可以将精力集中在应用程序逻辑上。
代理编程模型通常用于创建数据管道或网络。 数据管道是一系列组件,每个组件执行一个参与更大目标的特定任务。 数据流管道中的每个组件从另一个组件接收消息时执行工作。 该工作的结果传递到管道或网络中的其他组件。 这些组件可以使用其他库(如并行模式库 (PPL))中的更细化的并发功能。
示例
下面的示例实现本主题前面所示的图示。
// 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);
}
该示例产生下面的输出:
agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.
下面的主题描述该示例中使用的功能。
相关主题
异步代理
描述异步代理在执行更大计算任务中的作用。异步消息块
描述由代理库提供的各种消息块类型。消息传递函数
描述由代理库提供的各种消息传递例程。如何:实现各种制造者-使用者模式
描述如何在您的应用程序中实现制造者-使用者模式。如何:为 call 和 transformer 类提供工作函数
演示了几种提供工作函数 concurrency::call 和 concurrency::transformer 类。如何:在数据管道中使用转换器
演示如何使用 concurrency::transformer 数据管道中的类。如何:在已完成的任务之间选择
演示如何使用 concurrency::choice 和 concurrency::join 选择第一项任务完成搜索算法类。如何:定期发送消息
演示如何使用 concurrency::timer 定期时间间隔发送消息的类。如何:使用消息块筛选器
演示如何使用筛选器使异步消息块接受或拒绝消息。并行模式库 (PPL)
描述如何在应用程序中使用各种并行模式(例如并行算法)。并发运行时
描述可简化并行编程的并发运行时,并包含指向相关主题的链接。