如何:在已完成的任务之间选择

此示例演示如何使用 concurrency::choiceconcurrency::join 类选择用于完成搜索算法的第一个任务。

示例

以下示例并行执行两种搜索算法,并选择要完成的第一种算法。 此示例定义 employee 类型,该类型保存员工的数字标识符和工资。 find_employee 函数查找具有所提供标识符或所提供工资的第一名员工。 find_employee 函数还处理没有员工具备所提供标识符或工资的情况。 wmain 函数创建 employee 对象数组,并搜索多个标识符和工资值。

该示例使用对象 choice 在以下情况之间进行选择:

  1. 存在具有所提供标识符的员工。

  2. 存在具有所提供工资的员工。

  3. 不存在具有所提供标识符或工资的员工。

对于前两种情况,该示例使用一个 concurrency::single_assignment 对象来保存标识符,并使用另一个 single_assignment 对象来保存薪水。 该示例对第三种情况使用 join 对象。 join 对象由两个附加 single_assignment 对象组成,一个对象用于不存在具有所提供标识符的员工的情况,另一个对象用于没有所提供工资的员工的情况。 当其中每位成员收到消息时,join 对象都会发送一条消息。 在本例中,当不存在具有所提供标识符或工资的员工时,join 对象会发送一条消息。

该示例使用 concurrency::structured_task_group 对象并行运行这两种搜索算法。 每项搜索任务都会写入其中一个 single_assignment 对象,以指示给定员工是否存在。 该示例使用 concurrency::receive 函数获取第一个缓冲区的索引,该缓冲区包含一条消息和一个 switch 块来打印结果。

// find-employee.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <array>
#include <iostream>
#include <random>

using namespace concurrency;
using namespace std;

// Contains information about an employee.
struct employee
{
   int id;
   float salary;
};

// Finds the first employee that has the provided id or salary.
template <typename T>
void find_employee(const T& employees, int id, float salary)
{
   // Holds the salary for the employee with the provided id.
   single_assignment<float> find_id_result;

   // Holds the id for the employee with the provided salary.
   single_assignment<int> find_salary_result;


   // Holds a message if no employee with the provided id exists.
   single_assignment<bool> id_not_found;

   // Holds a message if no employee with the provided salary exists.
   single_assignment<bool> salary_not_found;

   // Create a join object for the "not found" buffers.
   // This join object sends a message when both its members holds a message 
   // (in other words, no employee with the provided id or salary exists).
   auto not_found = make_join(&id_not_found, &salary_not_found);


   // Create a choice object to select among the following cases:
   // 1. An employee with the provided id exists.
   // 2. An employee with the provided salary exists.
   // 3. No employee with the provided id or salary exists.
   auto selector = make_choice(&find_id_result, &find_salary_result, &not_found);
   

   // Create a task that searches for the employee with the provided id.
   auto search_id_task = make_task([&]{
      auto result = find_if(begin(employees), end(employees), 
         [&](const employee& e) { return e.id == id; });
      if (result != end(employees))
      {
         // The id was found, send the salary to the result buffer.
         send(find_id_result, result->salary);
      }
      else
      {
         // The id was not found.
         send(id_not_found, true);
      }
   });

   // Create a task that searches for the employee with the provided salary.
   auto search_salary_task = make_task([&]{
      auto result = find_if(begin(employees), end(employees), 
         [&](const employee& e) { return e.salary == salary; });
      if (result != end(employees))
      {
         // The salary was found, send the id to the result buffer.
         send(find_salary_result, result->id);
      }
      else
      {
         // The salary was not found.
         send(salary_not_found, true);
      }
   });

   // Use a structured_task_group object to run both tasks.
   structured_task_group tasks;
   tasks.run(search_id_task);
   tasks.run(search_salary_task);

   wcout.setf(ios::fixed, ios::fixed);
   wcout.precision(2);

   // Receive the first object that holds a message and print a message.
   int index = receive(selector);
   switch (index)
   {
   case 0:
      wcout << L"Employee with id " << id << L" has salary " 
            << receive(find_id_result);
      break;
   case 1:
      wcout << L"Employee with salary " << salary << L" has id " 
            << receive(find_salary_result);
      break;
   case 2:
      wcout << L"No employee has id " << id << L" or salary " << salary;
      break;
   default:
      __assume(0);
   }
   wcout << L'.' << endl;
   
   // Cancel any active tasks and wait for the task group to finish.
   tasks.cancel();
   tasks.wait();
}

int wmain()
{
   // Create an array of employees and assign each one a 
   // random id and salary.

   array<employee, 10000> employees;
   
   mt19937 gen(15);
   const float base_salary = 25000.0f;
   for (int i = 0; i < employees.size(); ++i)
   {
      employees[i].id = gen()%100000;

      float bonus = static_cast<float>(gen()%5000);
      employees[i].salary = base_salary + bonus;
   }

   // Search for several id and salary values.

   find_employee(employees, 14758, 30210.00);
   find_employee(employees, 340, 29150.00);
   find_employee(employees, 61935, 29255.90);
   find_employee(employees, 899, 31223.00);
}

本示例生成以下输出。

Employee with id 14758 has salary 27780.00.
Employee with salary 29150.00 has id 84345.
Employee with id 61935 has salary 29905.00.
No employee has id 899 or salary 31223.00.

此示例使用 concurrency::make_choice 帮助程序函数创建 choice 对象,并使用 concurrency::make_join 帮助程序函数创建 join 对象。

编译代码

复制示例代码,并将它粘贴到 Visual Studio 项目中,或粘贴到名为 find-employee.cpp 的文件中,再在 Visual Studio 命令提示符窗口中运行以下命令。

cl.exe /EHsc find-employee.cpp

另请参阅

异步代理库
异步消息块
消息传递函数
choice 类
join 类