Отмена в библиотеке параллельных шаблонов
В этом документе рассматривается роль операции отмены в библиотеке параллельных шаблонов (PPL), а также объясняется, как отменить параллельную работу и как определить, что параллельная работа отменена.
Примечание
Среда выполнения использует обработку исключений для реализации отмены.Не перехватывайте и не обрабатывайте эти исключения в своем коде.Кроме того, рекомендуется в теле функций для ваших задач писать код, безопасный в отношении исключений.Например, можно использовать шаблон Получение ресурса есть инициализация (Resource Acquisition Is Initialization, RAII) для обеспечения корректной обработки ресурсов при возникновении исключения в теле задачи.Полный пример с использованием шаблона RAII для очистки ресурса в отменяемой задаче см. в разделе Пошаговое руководство. Удаление задач из потоков пользовательского интерфейса.
Ключевые моменты
Отмена выполняется совместно и обеспечивает координацию между кодом, запрашивающим отмену запросов, и задачей, которая отвечает за отмену.
Если возможно, используйте токены отмены, чтобы отменить работу. Класс concurrency::cancellation_token определяет токен отмены.
При использовании токенов отмены используйте метод concurrency::cancellation_token_source::cancel, чтобы инициировать отмену и функции concurrency::is_task_cancellation_requested и concurrency::cancel_current_task, чтобы реагировать на отмену.
Отмена не происходит немедленно. Хотя новая работа не запускается, если задача или группа задач отменяются, выполняющаяся работа должна проверять и реагировать на отмену.
Продолжение, основанное на значении, наследует токен отмены своей предшествующей задачи. Продолжение задач никогда не наследует токен своей предшествующей задачи.
Используйте метод concurrency::cancellation_token::none при вызове конструктора или функции, которая принимает объект cancellation_token, но вы хотите, чтобы операция поддерживала отмену. Кроме того, если вы не передаете токен отмены в конструктор concurrency::task или функцию concurrency::create_task, эта задача не будет поддерживать отмену.
В этом документе
Деревья параллельной работы
Отмена параллельных задач
Использование токена отмены для отмены параллельной работы
Использование метода cancel для отмены параллельной работы
Использование исключений для отмены параллельной работы
Отмена параллельных алгоритмов
Когда не следует использовать отмену
Деревья параллельной работы
В PPL для управления гибкими задачами и вычислениями используются задачи и группы задач. Можно вкладывать группы задач одна в другую, чтобы создавать деревья параллельной работы. На следующем рисунке показано дерево параллельной работы. На этом рисунке tg1 и tg2 представляют группы задач; t1, t2, t3, t4 и t5 представляют собой работы, которые выполняют группы задач.
В следующем примере показан код, необходимый для создания дерева, изображенного на рисунке. В этом примере tg1 и tg2 являются объектами concurrency::structured_task_group; а t1, t2, t3, t4 и t5 — объектами concurrency::task_handle.
// 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();
}
Можно также использовать класс concurrency::task_group для создания подобного дерева работы. Класс concurrency::task также поддерживает понятие дерева работы. Однако дерево task является деревом зависимостей. В дереве task следующие задания выполняются после текущих. В дереве группы задач внутренняя работа завершается до внешней работы. Дополнительные сведения о различиях между задачами и группами задач см. в разделе Параллелизм задач (среда выполнения с параллелизмом).
[Наверх]
Отмена параллельных задач
Существует несколько способов отмены параллельной работы. Предпочтительный способ — использование токена отмены. Группы задач также поддерживают метод concurrency::task_group::cancel и метод concurrency::structured_task_group::cancel. Еще один способ — создать исключение в теле рабочей функции задачи. Какой бы метод вы ни выбрали, нужно понимать, что отмена не происходит немедленно. Хотя новая работа не запускается, если задача или группа задач отменяются, выполняющаяся работа должна проверять и реагировать на отмену.
Дополнительные примеры отмены параллельных задач см. в разделах Пошаговое руководство. Подключение с использованием задач и HTTP-запросов XML, Практическое руководство. Использование отмены для выхода из параллельного цикла и Практическое руководство. Использование обработки исключений для выхода из параллельного цикла.
Использование токена отмены для отмены параллельной работы
Классы task, task_group и structured_task_group поддерживают отмену посредством использования токенов отмены. PPL определяет классы concurrency::cancellation_token_source и concurrency::cancellation_token для этой цели. При использовании токена отмены чтобы отменить работу, среда выполнения не запускает новую работу, подписанную на этот токен. Уже выполняющаяся работа может отслеживать свой токен отмены и останавливаться, когда может.
Чтобы инициировать отмену, вызовите метод concurrency::cancellation_token_source::cancel. Можно реагировать на отмену следующими способами:
Для объектов task используйте функции concurrency::is_task_cancellation_requested и concurrency::cancel_current_task. cancel_current_task отменяет текущую задачу и любое ее продолжение, основанное на значении. (Он не отменяет токен отмены, связанный с задачей или ее продолжениями.)
Для групп задач и параллельных алгоритмов используйте функцию concurrency::is_current_task_group_canceling, чтобы отследить отмену и как можно скорее вернуться из тела задачи, когда эта функция возвращает true. (Не вызывайте cancel_current_task из группы задач.)
В следующем примере показан первый простой шаблон для отмены задачи. Тело задачи время от времени проверят отмену внутри цикла.
// task-basic-cancellation.cpp
// compile with: /EHsc
#include <ppltasks.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 (is_task_cancellation_requested())
{
// 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 создает исключение; поэтому нет необходимости явно возвращаться из текущего цикла или функции.
Совет
Кроме того, можно вызвать функцию concurrency::interruption_point вместо is_task_cancellation_requested и cancel_current_task.
Необходимо вызвать cancel_current_task при реагировании на отмену, поскольку она переводит задачу в отмененное состояние. Если вы вернулись раньше вместо вызова cancel_current_task, операция переходит в состояние завершения, и все продолжения, основанные на значении, выполняются.
Предупреждение
Никогда не генерируйте task_canceled в коде.Вызовите метод cancel_current_task в качестве альтернативы.
Когда задача заканчивается в отмененном состоянии, метод concurrency::task::get создает concurrency::task_canceled. (И, наоборот, concurrency::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 или функцию concurrency::create_task, эта задача не будет поддерживать отмену.Кроме того, необходимо передать один и тот же токен отмены в конструктор всех вложенных задач (т. е. задач, которые создаются в теле другой задачи), чтобы отменить все задачи параллельно.
Может понадобиться выполнить собственный код, когда токен отмены отменен. Например, если пользователь выбирает в интерфейсе пользователя кнопку Отмена, чтобы отменить операцию, можно отключить эту кнопку до тех пор, пока пользователь не начнет другую операцию. В следующем примере показано, как использовать метод concurrency::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 в задачу продолжения, продолжение наследует токен отмены из предшествующей задачи следующими способами:
Продолжение, основанное на значении, всегда наследует токен отмены предшествующей задачи.
Продолжение на основе задач никогда не наследует токен отмены предшествующей задачи. Единственный способ сделать продолжение на основе задач отменяемым — явно передать токен отмены.
Эти поведения не изменяются из-за прерванных задач (тех, которые создали исключение). В этом случае продолжение на основе значения отменяется, продолжение задачи не отменяется.
Предупреждение
Задача, которая создается внутри другой задачи (вложенная задача), не наследует токен отмены от родительской задачи.Только продолжение, основанное на значении, наследует токен отмены своей предшествующей задачи.
Совет
Используйте метод concurrency::cancellation_token::none, когда вызываете конструктор или функцию, которая принимает объект cancellation_token, и хотите, чтобы операция поддерживала отмену.
Также можно предоставить токен отмены конструктору объекта task_group или structured_task_group. Важным аспектом является то, что дочерние группы задач наследуют этот токен отмены. Пример, демонстрирующий эту концепцию с помощью использования функции concurrency::run_with_cancellation_token для вызова parallel_for, см. в разделе Отмена параллельных алгоритмов далее в этом документе.
[Наверх]
Токены отмены и композиция задач
Функции concurrency::when_all и concurrency::when_any помогают объединять несколько задач для реализации общих шаблонов. В этом разделе описывается, как эти функции работают с токенами отмены.
Если предоставляется токен отмены для любой из функций when_all и when_any, эта функция отменяется, только если этот токен отмены отменен, или одна из участвующих задач заканчивается в отмененном состоянии или создает исключение.
Функция when_all наследует токен отмены от каждой задачи, формирующий общую операцию, если вы не предоставите ей токен отмены. Задача возвращаемая из when_all отменена, когда любой из этих токенов отменен, или хотя бы одна из участвующих задач еще не началась или выполняется. Такое же поведение возникает, когда одна из задач создает исключение: задача, возвращаемая when_all, немедленно отменяется с этим исключением.
Среда выполнения выбирает токен отмены для задачи, которая возвращается функцией when_any, когда эта задача завершается. Если ни одна из участвующих задач не закончилась в завершенном состоянии или одна или более задач создали исключения, одна из создавших исключение задач выбирается для выполнения when_any, и ее токен выбирается как токен для окончания задачи. Если более чем одна задача заканчивается в завершенном состоянии, задача, возвращаемая when_any, заканчивается в завершенном состоянии. Среда выполнения пытается выбрать завершенную задачу, токен которой не отменен во время завершения, чтобы задача, которая возвращается when_any, не отменялась сразу же, даже если остальные выполняющиеся задачи могут завершиться позднее.
[Наверх]
Использование метода cancel для отмены параллельной работы
Методы concurrency::task_group::cancel и concurrency::structured_task_group::cancel задают для группы задач отмененное состояние. После вызова метода cancel группа задач не начинает выполнение следующих задач. Методы cancel могут вызываться несколькими дочерними задачами. Отмененная задача заставляет методы concurrency::task_group::wait и concurrency::structured_task_group::wait возвращать значение concurrency::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();
В этом примере проверка отмены выполняется каждую сотую итерацию цикла задач. Частота проверки отмены зависит от объема работы, выполняемой задачей, и тем, насколько быстро задачи должны реагировать на отмену.
Если отсутствует доступ к объекту родительской группы задач, следует вызвать функцию concurrency::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 и concurrency::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
*/
В следующем примере метод concurrency::structured_task_group::run_and_wait используется для вызова алгоритма parallel_for. Метод 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;
}
В результате выполнения примера получается следующий результат:
В следующем примере обработка исключений используется для отмены цикла 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;
}
В результате выполнения примера получается следующий результат:
В следующем примере логический флаг координирует отмену в цикле 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.
}
});
Каждый метод отмены имеет свои преимущества. Необходимо выбирать метод, соответствующий конкретным потребностям пользователя.
[Наверх]
Когда не следует использовать отмену
Отмену можно использовать, когда каждый член группы связанных задач может своевременно выполнить выход. Однако в некоторых сценариях приложение не позволяет использовать отмену. Например, поскольку отмена задач осуществляется совместно, общий набор задач не будет отменен, если одна из задач заблокирована. Например, если выполнение одной из задач не было начато, но эта задача разблокирует другую активную задачу, выполнение не начнется, если группа задач отменена. Это может стать причиной возникновения в приложении взаимоблокировки. Еще один пример ситуации, когда использование отмены может быть неуместно: задача отменяется, но ее дочерняя задача выполняет важную операцию, например высвобождение ресурса. Так как при отмене родительской задачи отменяется весь набор задач, эта операция выполнена на будет. Пример, иллюстрирующий этот момент, см. в разделе Узнайте, как отмена и обработка исключений влияет на уничтожение объектов документа "Лучшие практические примеры библиотеки шаблонов параллельного программирования".
[Наверх]
Связанные разделы
Название |
Описание |
---|---|
Практическое руководство. Использование отмены для выхода из параллельного цикла |
Показывает использование отмены для реализации алгоритма параллельного поиска. |
Практическое руководство. Использование обработки исключений для выхода из параллельного цикла |
Показывает, как использовать класс task_group для написания алгоритма поиска по простой древесной структуре. |
Описывает обработку в среде выполнения исключений, созданных группами задач, упрощенными задачами и асинхронными агентами, а также реагирование на исключения в приложениях. |
|
Описывает, как задачи связаны с группами задач и как использовать в приложениях неструктурированные и структурированные задачи. |
|
Описывает параллельные алгоритмы, одновременно выполняющие работу с коллекциями данных. |
|
Содержит общие сведения о библиотеке параллельных шаблонов. |
Справочные сведения
Класс task (среда выполнения с параллелизмом)