欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > windows C++ 并行编程-PPL 中的取消操作(四)

windows C++ 并行编程-PPL 中的取消操作(四)

2024/10/25 1:36:25 来源:https://blog.csdn.net/m0_72813396/article/details/141730338  浏览:    关键词:windows C++ 并行编程-PPL 中的取消操作(四)

并行模式库 (PPL) 中取消操作的角色、如何取消并行工作以及如何确定取消并行工作的时间。

运行时使用异常处理实现取消操作。 请勿在代码中捕捉或处理这些异常。 此外,还建议你在任务的函数体中编写异常安全的代码。 例如,可以使用获取资源即初始化 (RAII) 模式,以确保在任务体中引发异常时正确处理资源。

使用异常来取消并行工作

要取消并行工作树,使用取消标记和 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:151617100185
*/

下面的示例使用 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;
}输出:
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.}
});

每个取消方法都有其他方法所没有的优点。 请选择适合你的特定需求的方法。

何时不使用取消

当一组相关任务中的每个成员可以及时退出时,使用取消是恰当的。 但是,在某些情况下取消可能不适合你的应用程序。 例如,由于任务取消是协作性的,如果任何单个任务被阻止,则无法取消整个任务集。 例如,如果一个任务尚未开始,但它取消阻止另一个活动任务,则在任务组已取消时,它将不能启动。 这会导致应用程序中发生死锁。 可能不适合使用取消的另一个示例是任务被取消,但其子任务会执行重要操作(如释放资源)。 因为在取消父任务时整个任务集也会被取消,所以将无法执行此操作。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com