演练:创建自定义消息块

本文档介绍如何创建按优先级排序传入消息的自定义消息块类型。

尽管内置的消息块类型提供许多不同的功能,但您可以创建自己的消息块类型,并对其进行自定义以满足应用程序的要求。 有关异步代理库提供的内置消息块类型的说明,请参见异步消息块

系统必备

在开始本演练之前,请阅读下列文档:

各节内容

本演练包含以下各节:

  • 设计自定义消息块

  • 定义 priority_buffer 类

  • 完整示例

设计自定义消息块

消息块参与发送和接收消息的操作。 发送消息的消息块称为“源块”。 接收消息的消息块称为“目标块”。 既发送又接收消息的消息块称为“传播器块”。 代理库使用抽象类 concurrency::ISource 表示源块和抽象类 concurrency::ITarget 来表示目标块。 用作源的消息块类型从 ISource 派生;用作目标的消息块类型从 ITarget 派生。

尽管您可以直接从 ISourceITarget 派生消息块类型,代理库也会定义三种基类以执行所有消息块类型通用的大部分功能,例如,处理错误以及以并发安全的方式将消息块连接在一起。 Concurrency::source_block 从派生类ISource ,并将消息发送到其他块。 Concurrency::target_block 从派生类ITarget和接收来自其他块的消息。 Concurrency::propagator_block 从派生类ISourceITarget和其他块,而且它发送邮件从其他块接收的消息。 建议您使用这三种基类处理基础结构细节,以便您可以关注消息块的行为。

source_blocktarget_blockpropagator_block 类是针对管理源和目标块之间连接(或链接)的类型和管理如何处理消息的类型参数化的模板。 代理库定义了两种类型执行链接管理的 concurrency::single_link_registryconcurrency::multi_link_registrysingle_link_registry 类允许消息块链接至一个源或一个目标。 multi_link_registry 类允许消息块链接至多个源或多个目标。 代理库定义了一个类执行邮件管理的 concurrency::ordered_message_processorordered_message_processor 类允许消息块按照其接收消息的顺序处理消息。

为了更好地理解消息块如何与它们的源和目标关联,请考虑以下示例。 本示例显示了声明的 concurrency::transformer 类。

template<
   class _Input,
   class _Output
>
class transformer : public propagator_block<
   single_link_registry<ITarget<_Output>>, 
   multi_link_registry<ISource<_Input>>
>;

transformer 类派生自 propagator_block,因此可同时充当源块和目标块。 它接受 _Input 类型的消息,发送 _Output 类型的消息。 transformer 类指定 single_link_registry 作为所有目标块的链接管理器,指定 multi_link_registry 作为所有源块的链接管理器。 因此,transformer 对象最多能有一个目标,但源的数量没有限制。

source_block 派生的类必须实现六个方法:propagate_to_any_targetsaccept_messagereserve_messageconsume_messagerelease_messageresume_propagation。 从 target_block 派生的类必须实现 propagate_message 方法,并可以选择实现 send_message 方法。 从 propagator_block 派生在功能上等效于同时从 source_blocktarget_block 派生。

propagate_to_any_targets 方法由运行时调用以异步或同步处理所有传入的消息以及传播所有传出的消息。 accept_message 方法由目标块调用以接受消息。 许多消息块类型(例如 unbounded_buffer)仅将消息发送给接收该消息的第一个目标。 因此,它会将消息的所有权转移给目标。 其他消息块类型,如 concurrency::overwrite_buffer,提供其目标块的每个邮件。 因此,overwrite_buffer 为其每个目标创建一份消息的副本。

reserve_messageconsume_messagerelease_messageresume_propagation 方法允许消息块参与消息保留。 当目标块收到消息并必须保留该消息以备日后使用时,则会调用 reserve_message 方法。 目标块保留消息后,它可以调用 consume_message 方法以使用该消息,或调用 release_message 方法以取消保留。 与 accept_message 方法相同,consume_message 的实现可以转移消息的所有权或返回消息的副本。 当目标块使用或释放保留的消息之后,运行时会调用 resume_propagation 方法。 通常,该方法会从队列中的下一条消息开始继续进行消息传播。

运行时调用 propagate_message 方法以从另一个块将消息异步传输到当前块。 send_message 方法与 propagate_message 类似,但它同步而不是异步将消息发送给目标块。 send_message 的默认实现拒绝所有传入消息。 如果消息未传递与目标块关联的可选筛选功能,则运行时不调用其中任一方法。 有关消息筛选器的更多信息,请参见异步消息块

Top

定义 priority_buffer 类

priority_buffer 类是自定义的消息块类型,它先按照优先级、然后按照接收消息的顺序排序传入的消息。 priority_buffer类类似于 concurrency::unbounded_buffer 类,因为它包含的消息队列,还因为它是作为源和目标消息块,可以同时具有多个源和多个目标。 但是,unbounded_buffer 仅按照其从源接收消息的顺序进行消息传播。

priority_buffer 类接收 std::tuple 类型的消息,该类型包含 PriorityType 和 Type 元素。 PriorityType 是指保留每条消息优先级的类型;Type 是指消息的数据部分。 priority_buffer 类发送 Type 类型的消息。 priority_buffer 类还管理两个消息队列:std::priority_queue 对象(用于传入消息)和 std::queue 对象(用于传出消息)。 当 priority_buffer 对象同时接收多个消息或在用户阅读任何消息之前接收多个消息时,按优先级排序消息会很有用。

除了从 propagator_block 派生的类必须实现的七个方法以外,priority_buffer 类还替换 link_target_notificationsend_message 方法。 priority_buffer 类还定义两个公共帮助程序方法(enqueuedequeue)以及一个专用帮助程序方法(propagate_priority_order)。

下面的过程介绍如何实现 priority_buffer 类。

定义 priority_buffer 类

  1. 创建一个 C++ 头文件并将其命名为 priority_buffer.h。 或者,您可以使用项目中的现有头文件。

  2. 在 priority_buffer.h 中,添加以下代码。

    #pragma once
    #include <agents.h>
    #include <queue>
    
  3. std命名空间定义的专用化 std::lessstd::greater 的作用于 concurrency::message 对象。

    namespace std 
    {
    // A specialization of less that tests whether the priority element of a 
    // message is less than the priority element of another message.
    template<class Type, class PriorityType>
    struct less<concurrency::message<tuple<PriorityType,Type>>*> 
    {  
       typedef concurrency::message<tuple<PriorityType, Type>> MessageType;
    
       bool operator()(const MessageType* left, const MessageType* right) const
       {  
          // apply operator< to the first element (the priority) 
          // of the tuple payload.
          return (get<0>(left->payload) < get<0>(right->payload));
       }
    };
    
    // A specialization of less that tests whether the priority element of a 
    // message is greater than the priority element of another message.
    template<class Type, class PriorityType>
    struct greater<concurrency::message<tuple<PriorityType, Type>>*> 
    {  
       typedef concurrency::message<std::tuple<PriorityType,Type>> MessageType;
    
       bool operator()(const MessageType* left, const MessageType* right) const
       {  
          // apply operator> to the first element (the priority) 
          // of the tuple payload.
          return (get<0>(left->payload) > get<0>(right->payload));
       }
    };
    
    }
    

    priority_buffer 类将 message 对象存储在 priority_queue 对象中。 这些类型专用化允许优先级队列根据它们的优先级排序消息。 优先级是 tuple 对象的第一个元素。

  4. concurrency 命名空间中,声明 priority_buffer 类。

    namespace concurrency 
    {
    template<class Type, 
             typename PriorityType = int,
             typename Pr = std::less<message<std::tuple<PriorityType, Type>>*>>
    class priority_buffer : 
       public propagator_block<multi_link_registry<ITarget<Type>>,
                               multi_link_registry<ISource<std::tuple<PriorityType, Type>>>>
    {  
    public:
    protected:
    private:
    };
    }
    

    priority_buffer 类派生自 propagator_block。 因此,它可以同时发送和接收消息。 priority_buffer 类可以具有多个接收 Type 类型消息的目标。 它还可以具有多个发送 tuple<PriorityType, Type> 类型消息的源。

  5. priority_buffer 类的 private 部分,添加以下成员变量。

    // Stores incoming messages. 
    // The type parameter Pr specifies how to order messages by priority.
    std::priority_queue<
       message<_Source_type>*, 
       std::vector<message<_Source_type>*>, 
       Pr
    > _input_messages;
    
    // Synchronizes access to the input message queue.
    critical_section _input_lock;
    
    // Stores outgoing messages.
    std::queue<message<_Target_type>*> _output_messages;
    

    priority_queue 对象保存传入消息;queue 对象保存传出消息。 priority_buffer 对象可以同时接收多个消息;critical_section 对象可同步访问输入消息队列。

  6. 在 private 部分,定义复制构造函数和赋值运算符。 这会阻止 priority_queue 对象赋值。

    // Hide assignment operator and copy constructor.
    priority_buffer const &operator =(priority_buffer const&);
    priority_buffer(priority_buffer const &);
    
  7. 在 public 部分,定义许多消息块类型通用的构造函数。 还需定义析构函数。

    // Constructs a priority_buffer message block.
    priority_buffer() 
    {       
       initialize_source_and_target();
    }
    
    // Constructs a priority_buffer message block with the given filter function.
    priority_buffer(filter_method const& filter)
    {
       initialize_source_and_target();
       register_filter(filter);
    }
    
    // Constructs a priority_buffer message block that uses the provided 
    // Scheduler object to propagate messages.
    priority_buffer(Scheduler& scheduler)
    {
       initialize_source_and_target(&scheduler);
    }
    
    // Constructs a priority_buffer message block with the given filter function 
    // and uses the provided Scheduler object to propagate messages.
    priority_buffer(Scheduler& scheduler, filter_method const& filter) 
    {
       initialize_source_and_target(&scheduler);
       register_filter(filter);
    }
    
    // Constructs a priority_buffer message block that uses the provided 
    // SchedulerGroup object to propagate messages.
    priority_buffer(ScheduleGroup& schedule_group)
    {
       initialize_source_and_target(NULL, &schedule_group);
    }
    
    // Constructs a priority_buffer message block with the given filter function 
    // and uses the provided SchedulerGroup object to propagate messages.
    priority_buffer(ScheduleGroup& schedule_group, filter_method const& filter)
    {
       initialize_source_and_target(NULL, &schedule_group);
       register_filter(filter);
    }
    
    // Destroys the message block.
    ~priority_buffer()
    {
       // Remove all links.
       remove_network_links();
    }
    
  8. 在 public 部分,定义 enqueuedequeue 方法。 这些帮助程序方法提供向 priority_buffer 对象发送以及从其处接收消息的替代方法。

    // Sends an item to the message block.
    bool enqueue(Type const& item)
    {
      return concurrency::asend<Type>(this, item);
    }
    
    // Receives an item from the message block.
    Type dequeue()
    {
      return receive<Type>(this);
    }
    
  9. 在 protected 部分,定义 propagate_to_any_targets 方法。

    // Transfers the message at the front of the input queue to the output queue
    // and propagates out all messages in the output queue.
    virtual void propagate_to_any_targets(message<_Target_type>*)
    {
       // Retrieve the message from the front of the input queue.
       message<_Source_type>* input_message = NULL;
       {
          critical_section::scoped_lock lock(_input_lock);
          if (_input_messages.size() > 0)
          {
             input_message = _input_messages.top();
             _input_messages.pop();
          }
       }
    
       // Move the message to the output queue.
       if (input_message != NULL)
       {
          // The payload of the output message does not contain the 
          // priority of the message.
          message<_Target_type>* output_message = 
             new message<_Target_type>(get<1>(input_message->payload));
          _output_messages.push(output_message);
    
          // Free the memory for the input message.
          delete input_message;
    
          // Do not propagate messages if the new message is not the head message.
          // In this case, the head message is reserved by another message block.
          if (_output_messages.front()->msg_id() != output_message->msg_id())
          {
             return;
          }
       }
    
       // Propagate out the output messages.
       propagate_priority_order();
    }
    

    propagate_to_any_targets 方法将位于输入队列前面的消息传送至输出队列,并传播输出队列中的所有消息。

  10. 在 protected 部分,定义 accept_message 方法。

    // Accepts a message that was offered by this block by transferring ownership
    // to the caller.
    virtual message<_Target_type>* accept_message(runtime_object_identity msg_id)
    {        
       message<_Target_type>* message = NULL;
    
       // Transfer ownership if the provided message identifier matches
       // the identifier of the front of the output message queue.
       if (!_output_messages.empty() && 
            _output_messages.front()->msg_id() == msg_id)
       {
          message = _output_messages.front();            
          _output_messages.pop();
       }
    
       return message;
    }
    

    当目标块调用 accept_message 方法时,priority_buffer 类会将消息的所有权转移给接受该消息的第一个目标块。 (这类似于 unbounded_buffer 的行为。)

  11. 在 protected 部分,定义 reserve_message 方法。

    // Reserves a message that was previously offered by this block.
    virtual bool reserve_message(runtime_object_identity msg_id)
    {
       // Allow the message to be reserved if the provided message identifier
       // is the message identifier of the front of the message queue.
       return (!_output_messages.empty() && 
                _output_messages.front()->msg_id() == msg_id);
    }
    

    当提供的消息标识符与位于队列前面的消息的标识符匹配时,priority_buffer 类允许目标块保留消息。 换句话说,目标可以保留该邮件,如果priority_buffer对象尚未收到一条附加消息,并且当前尚不传播。

  12. 在 protected 部分,定义 consume_message 方法。

    // Transfers the message that was previously offered by this block 
    // to the caller. The caller of this method is the target block that 
    // reserved the message.
    virtual message<Type>* consume_message(runtime_object_identity msg_id)
    {
       // Transfer ownership of the message to the caller.
       return accept_message(msg_id);
    }
    

    目标块调用 consume_message 以转移其保留消息的所有权。

  13. 在 protected 部分,定义 release_message 方法。

    // Releases a previous message reservation.
    virtual void release_message(runtime_object_identity msg_id)
    {
       // The head message must be the one that is reserved. 
       if (_output_messages.empty() || 
           _output_messages.front()->msg_id() != msg_id)
       {
          throw message_not_found();
       }
    }
    

    目标块调用 release_message 以取消它对消息的保留。

  14. 在 protected 部分,定义 resume_propagation 方法。

    // Resumes propagation after a reservation has been released.
    virtual void resume_propagation()
    {
       // Propagate out any messages in the output queue.
       if (_output_messages.size() > 0)
       {
          async_send(NULL);
       }
    }
    

    当目标块使用或释放保留的消息之后,运行时调用 resume_propagation。 该方法传播输出队列中的所有消息。

  15. 在 protected 部分,定义 link_target_notification 方法。

    // Notifies this block that a new target has been linked to it.
    virtual void link_target_notification(ITarget<_Target_type>*)
    {
       // Do not propagate messages if a target block reserves
       // the message at the front of the queue.
       if (_M_pReservedFor != NULL)
       {
          return;
       }
    
       // Propagate out any messages that are in the output queue.
       propagate_priority_order();
    }
    

    _M_pReservedFor 成员变量由 source_block 基类定义。 该成员变量所指向的目标块(如果有)始终保留位于输出队列前面的消息。 当新目标链接至 priority_buffer 对象时,运行时会调用 link_target_notification。 如果没有目标存放保留消息,则该方法会传播输出队列中的所有消息。

  16. 在 private 部分,定义 propagate_priority_order 方法。

    // Propagates messages in priority order.
    void propagate_priority_order()
    {
       // Cancel propagation if another block reserves the head message.
       if (_M_pReservedFor != NULL)
       {
          return;
       }
    
       // Propagate out all output messages. 
       // Because this block preserves message ordering, stop propagation
       // if any of the messages are not accepted by a target block.
       while (!_output_messages.empty())
       {
          // Get the next message.
          message<_Target_type> * message = _output_messages.front();
    
          message_status status = declined;
    
          // Traverse each target in the order in which they are connected.
          for (target_iterator iter = _M_connectedTargets.begin(); 
               *iter != NULL; 
               ++iter)
          {
             // Propagate the message to the target.
             ITarget<_Target_type>* target = *iter;
             status = target->propagate(message, this);
    
             // If the target accepts the message then ownership of message has 
             // changed. Do not propagate this message to any other target.
             if (status == accepted)
             {
                break;
             }
    
             // If the target only reserved this message, we must wait until the 
             // target accepts the message.
             if (_M_pReservedFor != NULL)
             {
                break;
             }
          }
    
          // If status is anything other than accepted, then the head message
          // was not propagated out. To preserve the order in which output 
          // messages are propagated, we must stop propagation until the head 
          // message is accepted.
          if (status != accepted)
          {
              break;
          }          
       }
    }
    

    该方法传播输出队列中的所有消息。 队列中的每一条消息提供给每一个目标块,直到其中一个目标块接受消息。 priority_buffer 类保留传出消息的顺序。 因此,当该方法提供任何其他消息给某目标块之前,该目标块必须接受输出队列中的第一条消息。

  17. 在 protected 部分,定义 propagate_message 方法。

    // Asynchronously passes a message from an ISource block to this block.
    // This method is typically called by propagator_block::propagate.
    virtual message_status propagate_message(message<_Source_type>* message, 
       ISource<_Source_type>* source)
    {
       // Accept the message from the source block.
       message = source->accept(message->msg_id(), this);
    
       if (message != NULL)
       {
          // Insert the message into the input queue. The type parameter Pr
          // defines how to order messages by priority.
          {
             critical_section::scoped_lock lock(_input_lock);
             _input_messages.push(message);
          }
    
          // Asynchronously send the message to the target blocks.
          async_send(NULL);
          return accepted;
       }
       else
       {
          return missed;
       }      
    }
    

    propagate_message 方法允许 priority_buffer 类充当消息接收程序或目标。 该方法接收提供的源块提供的消息,并将该消息插入优先级队列中。 propagate_message 方法然后将所有输出消息异步发送给目标块。

    运行时调用此方法时,调用 concurrency::asend 函数或消息块时连接到其他消息块。

  18. 在 protected 部分,定义 send_message 方法。

    // Synchronously passes a message from an ISource block to this block.
    // This method is typically called by propagator_block::send.
    virtual message_status send_message(message<_Source_type>* message,
       ISource<_Source_type>* source)
    {
       // Accept the message from the source block.
       message = source->accept(message->msg_id(), this);
    
       if (message != NULL)
       {
          // Insert the message into the input queue. The type parameter Pr
          // defines how to order messages by priority.
          {
             critical_section::scoped_lock lock(_input_lock);
             _input_messages.push(message);
          }
    
          // Synchronously send the message to the target blocks.
          sync_send(NULL);
          return accepted;
       }
       else
       {
          return missed;
       }      
    }
    

    send_message 方法与 propagate_message 类似。 但它同步而不是异步发送输出消息。

    运行时调用此方法同步发送期间,例如,当您调用 concurrency::send 函数。

priority_buffer 类包含构造函数重载,许多消息块类型都具有这些重载。 某个构造函数重载采用 concurrency::Schedulerconcurrency::ScheduleGroup 对象,可通过特定的任务计划程序进行管理的消息块。 其他构造函数重载可获取筛选功能。 筛选功能允许消息块根据消息的负载接受或拒绝消息。 有关消息筛选器的更多信息,请参见异步消息块。 有关任务计划程序的更多信息,请参见任务计划程序(并发运行时)

因为priority_buffer类订单按优先级的邮件,然后接收消息的顺序,此类是最有用时收到消息以异步方式,例如,当您调用 concurrency::asend 函数或消息块时连接到其他消息块。

Top

完整示例

下面的示例显示 priority_buffer 类的完整定义。

// priority_buffer.h
#pragma once
#include <agents.h>
#include <queue>

namespace std 
{
// A specialization of less that tests whether the priority element of a 
// message is less than the priority element of another message.
template<class Type, class PriorityType>
struct less<concurrency::message<tuple<PriorityType,Type>>*> 
{  
   typedef concurrency::message<tuple<PriorityType, Type>> MessageType;

   bool operator()(const MessageType* left, const MessageType* right) const
   {  
      // apply operator< to the first element (the priority) 
      // of the tuple payload.
      return (get<0>(left->payload) < get<0>(right->payload));
   }
};

// A specialization of less that tests whether the priority element of a 
// message is greater than the priority element of another message.
template<class Type, class PriorityType>
struct greater<concurrency::message<tuple<PriorityType, Type>>*> 
{  
   typedef concurrency::message<std::tuple<PriorityType,Type>> MessageType;

   bool operator()(const MessageType* left, const MessageType* right) const
   {  
      // apply operator> to the first element (the priority) 
      // of the tuple payload.
      return (get<0>(left->payload) > get<0>(right->payload));
   }
};

}

namespace concurrency 
{
// A message block type that orders incoming messages first by priority, 
// and then by the order in which messages are received. 
template<class Type, 
         typename PriorityType = int,
         typename Pr = std::less<message<std::tuple<PriorityType, Type>>*>>
class priority_buffer : 
   public propagator_block<multi_link_registry<ITarget<Type>>,
                           multi_link_registry<ISource<std::tuple<PriorityType, Type>>>>
{  
public:
   // Constructs a priority_buffer message block.
   priority_buffer() 
   {       
      initialize_source_and_target();
   }

   // Constructs a priority_buffer message block with the given filter function.
   priority_buffer(filter_method const& filter)
   {
      initialize_source_and_target();
      register_filter(filter);
   }

   // Constructs a priority_buffer message block that uses the provided 
   // Scheduler object to propagate messages.
   priority_buffer(Scheduler& scheduler)
   {
      initialize_source_and_target(&scheduler);
   }

   // Constructs a priority_buffer message block with the given filter function 
   // and uses the provided Scheduler object to propagate messages.
   priority_buffer(Scheduler& scheduler, filter_method const& filter) 
   {
      initialize_source_and_target(&scheduler);
      register_filter(filter);
   }

   // Constructs a priority_buffer message block that uses the provided 
   // SchedulerGroup object to propagate messages.
   priority_buffer(ScheduleGroup& schedule_group)
   {
      initialize_source_and_target(NULL, &schedule_group);
   }

   // Constructs a priority_buffer message block with the given filter function 
   // and uses the provided SchedulerGroup object to propagate messages.
   priority_buffer(ScheduleGroup& schedule_group, filter_method const& filter)
   {
      initialize_source_and_target(NULL, &schedule_group);
      register_filter(filter);
   }

   // Destroys the message block.
   ~priority_buffer()
   {
      // Remove all links.
      remove_network_links();
   }

   // Sends an item to the message block.
   bool enqueue(Type const& item)
   {
     return concurrency::asend<Type>(this, item);
   }

   // Receives an item from the message block.
   Type dequeue()
   {
     return receive<Type>(this);
   }

protected:
   // Asynchronously passes a message from an ISource block to this block.
   // This method is typically called by propagator_block::propagate.
   virtual message_status propagate_message(message<_Source_type>* message, 
      ISource<_Source_type>* source)
   {
      // Accept the message from the source block.
      message = source->accept(message->msg_id(), this);

      if (message != NULL)
      {
         // Insert the message into the input queue. The type parameter Pr
         // defines how to order messages by priority.
         {
            critical_section::scoped_lock lock(_input_lock);
            _input_messages.push(message);
         }

         // Asynchronously send the message to the target blocks.
         async_send(NULL);
         return accepted;
      }
      else
      {
         return missed;
      }      
   }

   // Synchronously passes a message from an ISource block to this block.
   // This method is typically called by propagator_block::send.
   virtual message_status send_message(message<_Source_type>* message,
      ISource<_Source_type>* source)
   {
      // Accept the message from the source block.
      message = source->accept(message->msg_id(), this);

      if (message != NULL)
      {
         // Insert the message into the input queue. The type parameter Pr
         // defines how to order messages by priority.
         {
            critical_section::scoped_lock lock(_input_lock);
            _input_messages.push(message);
         }

         // Synchronously send the message to the target blocks.
         sync_send(NULL);
         return accepted;
      }
      else
      {
         return missed;
      }      
   }

   // Accepts a message that was offered by this block by transferring ownership
   // to the caller.
   virtual message<_Target_type>* accept_message(runtime_object_identity msg_id)
   {        
      message<_Target_type>* message = NULL;

      // Transfer ownership if the provided message identifier matches
      // the identifier of the front of the output message queue.
      if (!_output_messages.empty() && 
           _output_messages.front()->msg_id() == msg_id)
      {
         message = _output_messages.front();            
         _output_messages.pop();
      }

      return message;
   }

   // Reserves a message that was previously offered by this block.
   virtual bool reserve_message(runtime_object_identity msg_id)
   {
      // Allow the message to be reserved if the provided message identifier
      // is the message identifier of the front of the message queue.
      return (!_output_messages.empty() && 
               _output_messages.front()->msg_id() == msg_id);
   }

   // Transfers the message that was previously offered by this block 
   // to the caller. The caller of this method is the target block that 
   // reserved the message.
   virtual message<Type>* consume_message(runtime_object_identity msg_id)
   {
      // Transfer ownership of the message to the caller.
      return accept_message(msg_id);
   }

   // Releases a previous message reservation.
   virtual void release_message(runtime_object_identity msg_id)
   {
      // The head message must be the one that is reserved. 
      if (_output_messages.empty() || 
          _output_messages.front()->msg_id() != msg_id)
      {
         throw message_not_found();
      }
   }

   // Resumes propagation after a reservation has been released.
   virtual void resume_propagation()
   {
      // Propagate out any messages in the output queue.
      if (_output_messages.size() > 0)
      {
         async_send(NULL);
      }
   }

   // Notifies this block that a new target has been linked to it.
   virtual void link_target_notification(ITarget<_Target_type>*)
   {
      // Do not propagate messages if a target block reserves
      // the message at the front of the queue.
      if (_M_pReservedFor != NULL)
      {
         return;
      }

      // Propagate out any messages that are in the output queue.
      propagate_priority_order();
   }

   // Transfers the message at the front of the input queue to the output queue
   // and propagates out all messages in the output queue.
   virtual void propagate_to_any_targets(message<_Target_type>*)
   {
      // Retrieve the message from the front of the input queue.
      message<_Source_type>* input_message = NULL;
      {
         critical_section::scoped_lock lock(_input_lock);
         if (_input_messages.size() > 0)
         {
            input_message = _input_messages.top();
            _input_messages.pop();
         }
      }

      // Move the message to the output queue.
      if (input_message != NULL)
      {
         // The payload of the output message does not contain the 
         // priority of the message.
         message<_Target_type>* output_message = 
            new message<_Target_type>(get<1>(input_message->payload));
         _output_messages.push(output_message);

         // Free the memory for the input message.
         delete input_message;

         // Do not propagate messages if the new message is not the head message.
         // In this case, the head message is reserved by another message block.
         if (_output_messages.front()->msg_id() != output_message->msg_id())
         {
            return;
         }
      }

      // Propagate out the output messages.
      propagate_priority_order();
   }

private:

   // Propagates messages in priority order.
   void propagate_priority_order()
   {
      // Cancel propagation if another block reserves the head message.
      if (_M_pReservedFor != NULL)
      {
         return;
      }

      // Propagate out all output messages. 
      // Because this block preserves message ordering, stop propagation
      // if any of the messages are not accepted by a target block.
      while (!_output_messages.empty())
      {
         // Get the next message.
         message<_Target_type> * message = _output_messages.front();

         message_status status = declined;

         // Traverse each target in the order in which they are connected.
         for (target_iterator iter = _M_connectedTargets.begin(); 
              *iter != NULL; 
              ++iter)
         {
            // Propagate the message to the target.
            ITarget<_Target_type>* target = *iter;
            status = target->propagate(message, this);

            // If the target accepts the message then ownership of message has 
            // changed. Do not propagate this message to any other target.
            if (status == accepted)
            {
               break;
            }

            // If the target only reserved this message, we must wait until the 
            // target accepts the message.
            if (_M_pReservedFor != NULL)
            {
               break;
            }
         }

         // If status is anything other than accepted, then the head message
         // was not propagated out. To preserve the order in which output 
         // messages are propagated, we must stop propagation until the head 
         // message is accepted.
         if (status != accepted)
         {
             break;
         }          
      }
   }

private:

   // Stores incoming messages. 
   // The type parameter Pr specifies how to order messages by priority.
   std::priority_queue<
      message<_Source_type>*, 
      std::vector<message<_Source_type>*>, 
      Pr
   > _input_messages;

   // Synchronizes access to the input message queue.
   critical_section _input_lock;

   // Stores outgoing messages.
   std::queue<message<_Target_type>*> _output_messages;

private:
   // Hide assignment operator and copy constructor.
   priority_buffer const &operator =(priority_buffer const&);
   priority_buffer(priority_buffer const &);
};

}

下面的示例同时执行多个asendconcurrency::receive 上的操作priority_buffer对象。

// priority_buffer.cpp
// compile with: /EHsc 
#include <ppl.h>
#include <iostream>
#include "priority_buffer.h"

using namespace concurrency;
using namespace std;

int wmain()
{
   // Concurrently perform a number of asend and receive operations
   // on a priority_buffer object.

   priority_buffer<int> pb;

   parallel_invoke(
      [&pb] { for (int i = 0; i < 25; ++i) asend(pb, make_tuple(2, 36)); },
      [&pb] { for (int i = 0; i < 25; ++i) asend(pb, make_tuple(0, 12)); },
      [&pb] { for (int i = 0; i < 25; ++i) asend(pb, make_tuple(1, 24)); },
      [&pb] { 
         for (int i = 0; i < 75; ++i) {
            wcout << receive(pb) << L' ';
            if ((i+1) % 25 == 0)
               wcout << endl;
         }
      }
   );
}

此示例产生下面的示例输出。

36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36
24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12

priority_buffer 类先按照优先级再按照接收消息的顺序排序消息。 在本示例中,消息的优先级数字越大,它在队列中越靠前。

Top

编译代码

复制的代码示例并将其粘贴在 Visual Studio 项目中,或粘贴的定义priority_buffer在名为的文件中的类 priority_buffer.h 和测试程序文件中的名为 priority_buffer.cpp ,然后在 Visual Studio 命令提示符窗口中运行以下命令。

cl.exe /EHsc priority_buffer.cpp

请参见

概念

异步消息块

消息传递函数

其他资源

并发运行时演练