Поделиться через


Отмена в библиотеке параллельных шаблонов

В этом документе рассматривается роль отмены в библиотеке параллельных шаблонов (PPL), а также объясняется, как отменить параллельную работу и как определить, что параллельная работа отменена.

Примечание.

Среда выполнения использует обработку исключений для реализации отмены. Не следует перехватывать или обрабатывать эти исключения в своем коде. Кроме того, рекомендуется писать безопасный в отношении исключений код в функциях для ваших задач. Например, можно использовать шаблон инициализации ресурсов (RAII), чтобы обеспечить правильную обработку ресурсов при возникновении исключения в тексте задачи. Полный пример, использующий шаблон RAII для очистки ресурса в отменяемой задаче, см. в пошаговом руководстве. Удаление работы из потока пользовательского интерфейса.

Основные моменты

  • Отмена выполняется совместно и обеспечивает координацию между кодом, запрашивающим отмену запросов, и задачей, которая отвечает за отмену.

  • Если возможно, используйте токены отмены, чтобы отменить работу. Класс параллелизма::cancellation_token определяет маркер отмены.

  • При использовании маркеров отмены используйте метод параллелизма::cancellation_token_source::cancel , чтобы инициировать отмену и функцию параллелизма::cancel_current_task для реагирования на отмену. Используйте метод параллелизма::cancellation_token::is_canceled, чтобы проверить, запрашивает ли любая другая задача отмены.

  • Отмена не происходит немедленно. Хотя новая работа не запущена, если задача или группа задач отменена, активная работа должна проверяться и реагировать на отмену.

  • Продолжение, основанное на значении, наследует токен отмены своей предшествующей задачи. Продолжение на основе задачи никогда не наследует токен своей предшествующей задачи.

  • Используйте метод параллелизма::cancellation_token::none при вызове конструктора или функции, принимающей cancellation_token объект, но не хотите, чтобы операция была отменена. Кроме того, если маркер отмены не передается конструктору параллелизма::task или функции параллелизма::create_task , эта задача не может быть отменена.

В этом документе

Деревья параллельных рабочих операций

В PPL для управления детализированными задачами и вычислениями используются задачи и группы задач. Вы можете вложить группы задач для формирования деревьев параллельной работы. На следующем рисунке показано дерево параллельной работы. На этом рисунке tg1 и tg2 представляют группы задач; t1, t2, t3, t4 и t5 представляют работы, которые выполняют группы задач.

Параллельное дерево работы.

В следующем примере показан код, который необходим для создания дерева на рисунке. В этом примере и являются объектами параллелизма::structured_task_group; t1, , t2t3, t4и t5 являются объектами параллелизма::task_handle.tg2 tg1

// task-tree.cpp
// compile with: /c /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

void create_task_tree()
{   
   // Create a task group that serves as the root of the tree.
   structured_task_group tg1;

   // Create a task that contains a nested task group.
   auto t1 = make_task([&] {
      structured_task_group tg2;
      
      // Create a child task.
      auto t4 = make_task([&] {
         // TODO: Perform work here.
      });

      // Create a child task.
      auto t5 = make_task([&] {
         // TODO: Perform work here.
      });

      // Run the child tasks and wait for them to finish.
      tg2.run(t4);
      tg2.run(t5);
      tg2.wait();
   });

   // Create a child task.
   auto t2 = make_task([&] {
      // TODO: Perform work here.
   });

   // Create a child task.
   auto t3 = make_task([&] {
      // TODO: Perform work here.
   });

   // Run the child tasks and wait for them to finish.
   tg1.run(t1);
   tg1.run(t2);
   tg1.run(t3);
   tg1.wait();   
}

Можно также использовать класс параллелизма::task_group для создания аналогичного дерева работы. Класс параллелизма::task также поддерживает понятие дерева работы. Однако дерево task является деревом зависимостей. В дереве task следующие задания выполняются после текущих. В дереве группы задач внутренняя работа завершается до внешней работы. Дополнительные сведения о различиях между задачами и группами задач см. в разделе "Параллелизм задач".

[В начало]

Отмена параллельных задач

Существует несколько способов отмены параллельной работы. Предпочтительный способ — использование токена отмены. Группы задач также поддерживают метод параллелизма::task_group::cancel и метод параллелизма::structured_task_group::cancel . Еще один способ — создать исключение в теле рабочей функции задачи. Какой бы метод вы ни выбрали, нужно понимать, что отмена не происходит немедленно. Хотя новая работа не запущена, если задача или группа задач отменена, активная работа должна проверяться и реагировать на отмену.

Дополнительные примеры, которые отменяют параллельные задачи, см. в пошаговом руководстве. Подключение с помощью задач и XML-HTTP-запросов, практическое руководство. Использование отмены для разрыва с параллельным циклом и практическое руководство. Использование обработки исключений для разрыва с параллельным циклом.

Использование маркера отмены для отмены параллельной работы

Классы task, task_group и structured_task_group поддерживают отмену посредством использования токенов отмены. PPL определяет классы параллелизма::cancellation_token_source и параллелизма::cancellation_token для этой цели. При использовании токена отмены, чтобы отменить работу, среда выполнения не запускает новую работу, которая подписывается на этот токен. Работа, которая уже активна, может использовать функцию-член is_canceled для отслеживания маркера отмены и остановки, когда она может.

Чтобы инициировать отмену , вызовите метод параллелизма::cancellation_token_source::cancel . Можно реагировать на отмену следующими способами.

  • Для task объектов используйте функцию параллелизма::cancel_current_task . cancel_current_task отменяет текущую задачу и любое ее продолжение, основанное на значении. (Он не отменяет маркер отмены, связанный с задачей или его продолжением.)

  • Для групп задач и параллельных алгоритмов используйте функцию параллелизма::is_current_task_group_canceling для обнаружения отмены и возврата как можно скорее из тела задачи при возврате trueэтой функции. (Не вызывайте cancel_current_task из группы задач.)

В следующем примере показан первый базовый шаблон для отмены задачи. Тело задачи время от времени проверяет, нет ли отмены внутри цикла.

// task-basic-cancellation.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <concrt.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

bool do_work()
{
    // Simulate work.
    wcout << L"Performing work..." << endl;
    wait(250);
    return true;
}

int wmain()
{
    cancellation_token_source cts;
    auto token = cts.get_token();

    wcout << L"Creating task..." << endl;

    // Create a task that performs work until it is canceled.
    auto t = create_task([&]
    {
        bool moreToDo = true;
        while (moreToDo)
        {
            // Check for cancellation.
            if (token.is_canceled())
            {
                // TODO: Perform any necessary cleanup here...

                // Cancel the current task.
                cancel_current_task();
            }
            else 
            {
                // Perform work.
                moreToDo = do_work();
            }
        }
    }, token);

    // Wait for one second and then cancel the task.
    wait(1000);

    wcout << L"Canceling task..." << endl;
    cts.cancel();

    // Wait for the task to cancel.
    wcout << L"Waiting for task to complete..." << endl;
    t.wait();

    wcout << L"Done." << endl;
}

/* Sample output:
    Creating task...
    Performing work...
    Performing work...
    Performing work...
    Performing work...
    Canceling task...
    Waiting for task to complete...
    Done.
*/

Функция cancel_current_task создает исключение, поэтому нет необходимости явно возвращаться из текущего цикла или функции.

Совет

Кроме того, вместо функции cancel_current_taskпараллелизма можно вызывать функцию параллелизма::interruption_point.

Необходимо вызвать cancel_current_task при реагировании на отмену, поскольку она переводит задачу в отмененное состояние. Если вы вернулись раньше вместо вызова cancel_current_task, операция переходит в состояние завершения, и все продолжения, основанные на значении, выполняются.

Внимание

Никогда не вызывайте исключение task_canceled из своего кода. Вместо него вызовите метод cancel_current_task.

Когда задача заканчивается в отмененном состоянии, метод concurrency::task:get вызывает параллелизм:::task_canceled. (И наоборот, параллелизм::task:wait возвращает task_status::canceled и не вызывает.) В следующем примере показано это поведение для продолжения на основе задач. Продолжение на основе задачи вызывается всегда, даже если предшествующая задача отменяется.

// task-canceled.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t1 = create_task([]() -> int
    {
        // Cancel the task.
        cancel_current_task();
    });

    // Create a continuation that retrieves the value from the previous.
    auto t2 = t1.then([](task<int> t)
    {
        try
        {
            int n = t.get();
            wcout << L"The previous task returned " << n << L'.' << endl;
        }
        catch (const task_canceled& e)
        {
            wcout << L"The previous task was canceled." << endl;
        }
    });

    // Wait for all tasks to complete.
    t2.wait();
}
/* Output:
    The previous task was canceled.
*/

Поскольку продолжения, основанные на значении, наследуют токен их предшествующей задачи, если они не были созданы с явным токеном, продолжения немедленно входят в отмененное состояние, даже если предшествующая задача по-прежнему выполняется. Поэтому любое исключение, создаваемое предшествующей задачей после отмены, не распространяется задачам продолжения. Отмена всегда переопределяет состояние предшествующей задачи. Следующий пример похож на предыдущий, но показывает поведение продолжения, основанного на значении.

auto t1 = create_task([]() -> int
{
    // Cancel the task.
    cancel_current_task();
});

// Create a continuation that retrieves the value from the previous.
auto t2 = t1.then([](int n)
{
    wcout << L"The previous task returned " << n << L'.' << endl;
});

try
{
    // Wait for all tasks to complete.
    t2.get();
}
catch (const task_canceled& e)
{
    wcout << L"The task was canceled." << endl;
}
/* Output:
    The task was canceled.
*/

Внимание

Если маркер отмены не передается task конструктору или функции параллелизма::create_task , эта задача не может быть отменена. Кроме того необходимо передать один и тот же токен отмены конструктору всех вложенных задач (т. е. задач, которые создаются в теле другой задачи), чтобы отменить все задачи одновременно.

Может понадобиться выполнить собственный код, когда токен отмены отменен. Например, если пользователь нажимает кнопку "Отмена" в пользовательском интерфейсе, чтобы отменить операцию, можно отключить эту кнопку, пока пользователь не начнет другую операцию. В следующем примере показано, как использовать метод параллелизма::cancellation_token::register_callback для регистрации функции обратного вызова, которая выполняется при отмене маркера отмены.

// task-cancellation-callback.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    cancellation_token_source cts;
    auto token = cts.get_token();

    // An event that is set in the cancellation callback.
    event e;

    cancellation_token_registration cookie;
    cookie = token.register_callback([&e, token, &cookie]()
    {
        wcout << L"In cancellation callback..." << endl;
        e.set();

        // Although not required, demonstrate how to unregister 
        // the callback.
        token.deregister_callback(cookie);
    });

    wcout << L"Creating task..." << endl;

    // Create a task that waits to be canceled.
    auto t = create_task([&e]
    {
        e.wait();
    }, token);

    // Cancel the task.
    wcout << L"Canceling task..." << endl;
    cts.cancel();

    // Wait for the task to cancel.
    t.wait();

    wcout << L"Done." << endl;
}
/* Sample output:
    Creating task...
    Canceling task...
    In cancellation callback...
    Done.
*/

Параллелизм задач документа объясняет разницу между продолжениями на основе значений и на основе задач. Если не предоставить объект cancellation_token задаче продолжения, продолжение наследует токен отмены из предшествующей задачи следующими способами.

  • Продолжение, основанное на значении, всегда наследует токен отмены предшествующей задачи.

  • Продолжение на основе задач никогда не наследует токен отмены предшествующей задачи. Единственный способ сделать продолжение на основе задач отменяемым — явно передать токен отмены.

Эти поведения не изменяются из-за сбоя задачи (той, которая создает исключение). В этом случае отменяется продолжение на основе значений; Продолжение на основе задач не отменяется.

Внимание

Задача, которая создается внутри другой задачи (вложенная задача), не наследует токен отмены от родительской задачи. Только продолжение, основанное на значении, наследует токен отмены своей предшествующей задачи.

Совет

Используйте метод параллелизма::cancellation_token::none при вызове конструктора или функции, принимающей cancellation_token объект, и вы не хотите, чтобы операция была отменена.

Также можно предоставить токен отмены конструктору объекта task_group или structured_task_group. Важным аспектом является то, что дочерние группы задач наследуют этот токен отмены. Пример, демонстрирующий эту концепцию с помощью функции параллелизма::run_with_cancellation_token для вызова parallel_for, см . в разделе "Отмена параллельных алгоритмов " далее в этом документе.

[В начало]

Токены отмены и композиция задач

Функции параллелизма::when_all и параллелизма::when_any помогают создавать несколько задач для реализации общих шаблонов. В этом разделе описывается, как эти функции работают с токенами отмены.

Если вы предоставляете маркер when_all when_any отмены либо функции, эта функция отменяет только при отмене маркера отмены, либо когда одна из задач участника заканчивается в отмененном состоянии или вызывает исключение.

Функция when_all наследует токен отмены от каждой задачи, формирующей общую операцию, если ей не предоставляется токен отмены. Задача, возвращаемая из when_all нее, отменяется при отмене любого из этих маркеров, и по крайней мере одна из задач участника еще не запущена или запущена. Аналогичное поведение возникает, когда одна из задач создает исключение — задача, возвращаемая из when_all нее, немедленно отменяется с этим исключением.

Среда выполнения выбирает токен отмены для задачи, которая возвращается из функции when_any, когда эта задача завершается. Если ни одна из участвующих задач не заканчивается в завершенном состоянии или одна или более задач создают исключение, одна из создавших исключение задач выбирается для выполнения when_any, а ее токен выбирается как токен для окончательной задачи. Если более чем одна задача заканчивается в завершенном состоянии, задача, возвращаемая when_any, заканчивается в завершенном состоянии. Среда выполнения пытается выбрать завершенную задачу, токен которой не отменяется во время завершения, чтобы задача, которая возвращается из when_any, не отменялась сразу же, даже если остальные выполняющиеся задачи могут завершиться позднее.

[В начало]

Использование метода отмены для отмены параллельной работы

Методы параллелизма::task_group::cancel и concurrency::structured_task_group::cancel задают группу задач в состояние отмены. После вызова метода cancel группа задач не начинает выполнение следующих задач. Методы cancel могут вызываться несколькими дочерними задачами. Отмененная задача приводит к тому, что методы параллелизма::task_group::wait и параллелизма::structured_task_group::wait возвращают параллелизм::canceled.

Если группа задач отменена, вызовы каждой дочерней задачи в среду выполнения могут активировать точку прерывания, что приводит к возникновению и перехвату внутреннего типа исключения для отмены активных задач. Среда выполнения с параллелизмом не определяет конкретные точки прерывания; они могут возникать в любом вызове среды выполнения. Среда выполнения должна обрабатывать исключения, которые она вызывает, чтобы выполнить отмену. Таким образом, не следует обрабатывать неизвестные исключения в теле задачи.

Если дочерняя задача выполняет длительную операцию и не выполняет вызовы, направленные в среду выполнения, необходимо периодически проверять ее для своевременной отмены и выхода. В следующем примере показывается один из способов определения, когда работа отменяется. Задача t4 отменяет родительскую группу задач при обнаружении ошибки. Задача t5 периодически вызывает метод structured_task_group::is_canceling для проверки отмены. Если родительская группа задач отменяется, задача t5 выводит сообщение и завершает работу.

structured_task_group tg2;

// Create a child task.
auto t4 = make_task([&] {
   // Perform work in a loop.
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work.
      // If the work function fails, cancel the parent task
      // and break from the loop.
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg2.cancel();
         break;
      }
   }
});

// Create a child task.
auto t5 = make_task([&] {
   // Perform work in a loop.
   for (int i = 0; i < 1000; ++i)
   {
      // To reduce overhead, occasionally check for 
      // cancelation.
      if ((i%100) == 0)
      {
         if (tg2.is_canceling())
         {
            wcout << L"The task was canceled." << endl;
            break;
         }
      }

      // TODO: Perform work here.
   }
});

// Run the child tasks and wait for them to finish.
tg2.run(t4);
tg2.run(t5);
tg2.wait();

В этом примере проверяется отмена каждые 100-й итерации цикла задач. Частота, с которой выполняется проверка отмены, зависит от объема работы, выполняемой задачей, и того, как быстро задачи должны реагировать на отмену.

Если у вас нет доступа к объекту родительской группы задач, вызовите функцию параллелизма::is_current_task_group_canceling , чтобы определить, отменена ли родительская группа задач.

Метод cancel влияет только на дочерние задачи. Например, если отменить группу задач tg1, показанную на рисунке дерева параллельной работы, будут затронуты все задачи в дереве (t1, t2, t3, t4 и t5). Если отменить вложенную группу задач tg2, будут затронуты только задачи t4 и t5.

При вызове метода cancel будут также отменены все дочерние группы задач. Однако отмена не повлияет ни на какие родительские объекты группы задач в дереве параллельной работы. В следующих примерах это демонстрируется с опорой на иллюстрацию дерева параллельной работы.

В первом из этих примеров создается рабочая функция для задачи t4, которая является дочерним элементом группы задач tg2. Эта рабочая функция вызывает функцию work в цикле. Если какой-либо вызов work завершается неудачно, задача отменяет свою родительскую группу задач. В результате группа задач tg2 переходит в отмененное состояние, но группа задач tg1 не отменяется.

auto t4 = make_task([&] {
   // Perform work in a loop.
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work.
      // If the work function fails, cancel the parent task
      // and break from the loop.
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg2.cancel();
         break;
      }
   }         
});

Второй пример аналогичен первому, но за тем исключением, что задача отменяет группу задач tg1. Это влияет на все задачи в дереве (t1, t2, t3, t4 и t5).

auto t4 = make_task([&] {
   // Perform work in a loop.
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work.
      // If the work function fails, cancel all tasks in the tree.
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg1.cancel();
         break;
      }
   }   
});

Класс structured_task_group не является потокобезопасным. Таким образом, дочерняя задача, которая вызывает метод своего родительского объекта structured_task_group, приводит к непредсказуемому поведению. Исключениями этого правила являются structured_task_group::cancel методы и параллелизм::structured_task_group:::is_canceling . Дочерняя задача может вызывать эти методы для отмены родительской группы задач и проверки на предмет отмены.

Внимание

Хотя можно использовать токен отмены, чтобы отменить работу, выполняемую группой задач, которая выполняется как дочерний элемент объекта task, невозможно использовать методы task_group::cancel или structured_task_group::cancel, чтобы отменить объекты task, выполняемые в группе задач.

[В начало]

Использование исключений для отмены параллельной работы

Использование токенов отмены и метода cancel более эффективно, чем обработка исключений при отмене дерева параллельной работы. Токены отмены и метод cancel отменяют задачу и все дочерние задачи сверху вниз. И наоборот, обработка исключений работает в режиме «снизу вверх» и необходимо отменять каждую дочернюю группу задач независимо, поскольку исключение распространяется вверх. В разделе "Обработка исключений" объясняется, как среда выполнения параллелизма использует исключения для связи с ошибками. Однако не все исключения указывают на ошибку. Например, алгоритм поиска может отменить связанную задачу при нахождении результата. Тем не менее, как упоминалось ранее, обработка исключений менее эффективна, чем использование метода cancel для отмены параллельной работы.

Внимание

Рекомендуется использовать исключения для отмены параллельной работы только при необходимости. Токены отмены и методы cancel группы задач более эффективны и менее подвержены возникновению ошибок.

При создании исключения в теле рабочей функции, передаваемой в группу задач, среда выполнения сохраняет это исключение и маршалирует его в контекст, ожидающий завершения этой группы задач. Как и в случае использования метода cancel, среда выполнения удаляет любые задачи, которые еще не были запущены, и не принимает новые задачи.

Третий пример напоминает второй, за исключением того, что задача t4 создает исключение для отмены группы задач tg2. В этом примере try-catch используется блок для проверки отмены, когда группа tg2 задач ожидает завершения дочерних задач. Как и в первом примере, в результате группа задач tg2 переходит в отмененное состояние, но группа задач tg1 не отменяется.

structured_task_group tg2;

// Create a child task.      
auto t4 = make_task([&] {
   // Perform work in a loop.
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work.
      // If the work function fails, throw an exception to 
      // cancel the parent task.
      bool succeeded = work(i);
      if (!succeeded)
      {
         throw exception("The task failed");
      }
   }         
});

// Create a child task.
auto t5 = make_task([&] {
   // TODO: Perform work here.
});

// Run the child tasks.
tg2.run(t4);
tg2.run(t5);

// Wait for the tasks to finish. The runtime marshals any exception
// that occurs to the call to wait.
try
{
   tg2.wait();
}
catch (const exception& e)
{
   wcout << e.what() << endl;
}

В четвертом примере используется обработка исключений для отмены всего дерева работы. В этом примере перехватывается исключение, когда группа задач tg1 ожидает завершения своих дочерних задач вместо группы задач tg2, ожидающей своих дочерних задач. Как и во втором примере, это приводит к переходу в отмененное состояние обеих групп задач в дереве, tg1 и tg2.

// Run the child tasks.
tg1.run(t1);
tg1.run(t2);
tg1.run(t3);   

// Wait for the tasks to finish. The runtime marshals any exception
// that occurs to the call to wait.
try
{
   tg1.wait();
}
catch (const exception& e)
{
   wcout << e.what() << endl;
}

Так как методы task_group::wait и structured_task_group::wait вызываются, когда дочерняя задача создает исключение, вы не получите от них возвращаемое значение.

[В начало]

Отмена параллельных алгоритмов

Параллельные алгоритмы в PPL (например, parallel_for) основаны на группах задач. Таким образом, многие из тех же способов можно использовать для для отмены параллельного алгоритма.

Следующие примеры иллюстрируют несколько способов отмены параллельного алгоритма.

В следующем примере функция run_with_cancellation_token используется для вызова алгоритма parallel_for. Функция run_with_cancellation_token принимает в качестве аргумента токен отмены и одновременно вызывает предоставленную рабочую функцию. Поскольку параллельные алгоритмы строятся на задачах, они наследуют токен отмены родительской задачи. Поэтому parallel_for может реагировать на отмену.

// cancel-parallel-for.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Call parallel_for in the context of a cancellation token.
    cancellation_token_source cts;
    run_with_cancellation_token([&cts]() 
    {
        // Print values to the console in parallel.
        parallel_for(0, 20, [&cts](int n)
        {
            // For demonstration, cancel the overall operation 
            // when n equals 11.
            if (n == 11)
            {
                cts.cancel();
            }
            // Otherwise, print the value.
            else
            {
                wstringstream ss;
                ss << n << endl;
                wcout << ss.str();
            }
        });
    }, cts.get_token());
}
/* Sample output:
    15
    16
    17
    10
    0
    18
    5
*/

В следующем примере для вызова parallel_for алгоритма используется метод параллелизма::structured_task_group::run_and_wait. Метод structured_task_group::run_and_wait ожидает завершения предоставленной задачи. Объект structured_task_group позволяет рабочей функции отменить задачу.

// To enable cancelation, call parallel_for in a task group.
structured_task_group tg;

task_group_status status = tg.run_and_wait([&] {
   parallel_for(0, 100, [&](int i) {
      // Cancel the task when i is 50.
      if (i == 50)
      {
         tg.cancel();
      }
      else
      {
         // TODO: Perform work here.
      }
   });
});

// Print the task group status.
wcout << L"The task group status is: ";
switch (status)
{
case not_complete:
   wcout << L"not complete." << endl;
   break;
case completed:
   wcout << L"completed." << endl;
   break;
case canceled:
   wcout << L"canceled." << endl;
   break;
default:
   wcout << L"unknown." << endl;
   break;
}

В этом примере формируются следующие данные:

The task group status is: canceled.

В следующем примере используется обработка исключений для отмены цикла parallel_for. Среда выполнения маршалирует исключение в вызывающий контекст.

try
{
   parallel_for(0, 100, [&](int i) {
      // Throw an exception to cancel the task when i is 50.
      if (i == 50)
      {
         throw i;
      }
      else
      {
         // TODO: Perform work here.
      }
   });
}
catch (int n)
{
   wcout << L"Caught " << n << endl;
}

В этом примере формируются следующие данные:

Caught 50

В следующем примере используется логический флаг для координации отмены в цикле parallel_for Каждая задача выполняется, поскольку в этом примере не используется метод cancel или обработка исключений, чтобы отменить весь набор задач. Таким образом, этот метод может использовать больше вычислительных ресурсов, чем механизм отмены.

// Create a Boolean flag to coordinate cancelation.
bool canceled = false;

parallel_for(0, 100, [&](int i) {
   // For illustration, set the flag to cancel the task when i is 50.
   if (i == 50)
   {
      canceled = true;
   }

   // Perform work if the task is not canceled.
   if (!canceled)
   {
      // TODO: Perform work here.
   }
});

Каждый метод отмены имеет свои преимущества по сравнению с другими. Выбирайте метод, который соответствует вашим конкретным требованиям.

[В начало]

Если не использовать отмену

Использование отмены подходит в тех случаях, когда каждый член группы связанных задач может выполнить выход своевременно. Однако существуют некоторые сценарии, в которых отмена может не подойти для вашего приложения. Например, поскольку отмена задач осуществляется совместно, весь набор задач не будет отменен, если одна из задач заблокирована. Например, если одна задача еще не запущена, но разблокирует другую активную задачу, эта задача не запустится, если отменяется группа задач. Это может вызвать взаимоблокировку в приложении. Второй пример, когда использование отмены может не подойти: задача отменяется, но ее дочерняя задача выполняет важную операцию, например высвобождение ресурса. Так как при отмене родительской задачи отменяется весь набор задач, эта операция не будет выполнена. Пример, демонстрирующий эту точку, см . в разделе "Общие сведения о том, как отмена и обработка исключений влияют на уничтожение объектов" в разделе "Рекомендации" в разделе библиотеки параллельных шаблонов.

[В начало]

Заголовок Description
Практическое руководство. Использование отмены для выхода из параллельного цикла Показывается, как использовать отмену для реализации алгоритма параллельного поиска.
Практическое руководство. Использование обработки исключений для выхода из параллельного цикла Здесь приводятся способы использования класса task_group для записи алгоритма поиска для базовой структуры дерева.
Обработка исключений В этом разделе описывается обработка в среде выполнения исключений, созданных группами задач, упрощенными задачами и асинхронными агентами, а также способы реагирования на исключения в приложениях.
Параллелизм задач В этом разделе описывается, как задачи связаны с группами задач и как можно использовать структурированные и неструктурированные задачи в приложениях.
Параллельные алгоритмы В этом разделе описываются параллельные алгоритмы, одновременно выполняющие работу с коллекциями данных.
Библиотека параллельных шаблонов Общие сведения о библиотеке параллельных задач.

Справочные материалы

Класс task (среда выполнения с параллелизмом)

Класс cancellation_token_source

Класс cancellation_token

Класс task_group

Класс structured_task_group

Функция parallel_for