Практическое руководство. Выбор среди завершенных задач

В это примере показано, как с помощью классов Concurrency::choice и Concurrency::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, чтобы указать, существует ли заданный сотрудник. В примере для получения индекса первого буфера, который содержит сообщение и блок switch для вывода результата, используется функция Concurrency::receive.

// 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(employees.begin(), employees.end(), 
         [&](const employee& e) { return e.id == id; });
      if (result != employees.end())
      {
         // 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(employees.begin(), employees.end(), 
         [&](const employee& e) { return e.salary == salary; });
      if (result != employees.end())
      {
         // 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 2010 следующую команду.

cl.exe /EHsc find-employee.cpp

См. также

Ссылки

Класс choice

Класс join

Основные понятия

Библиотека асинхронных агентов

Асинхронные блоки сообщений

Функции передачи сообщений