windows C++-并行编程-PPL任务并行(二)
延续任务
在异步编程中,一个异步操作在完成时调用另一个操作并将数据传递到其中的情况非常常见。 传统上,这使用回调方法来完成。 在并发运行时中,延续任务提供了同样的功能。 延续任务(也简称为“延续”)是一个异步任务,由另一个任务(称为先行)在完成时调用。 使用延续可以:
- 将数据从前面的任务传递到延续;
- 指定调用或不调用延续所依据的精确条件;
- 在延续启动之前取消延续,或在延续正在运行时以协作方式取消延续;
- 提供有关应如何计划延续的提示。 (这仅适用于通用 Windows 平台 (UWP) 应用;
- 从同一前面的任务中调用多个延续;
- 在多个先行任务中的全部或任意任务完成时调用一个延续;
- 将延续依次相连,形成任意长度;
- 使用延续来处理先行引发的异常;
这些功能使你可以在第一个任务完成时执行一个或多个任务。 例如,可以创建在第一个任务从磁盘读取文件之后压缩文件的延续。
下面的示例将上面的示例修改为使用 concurrency::task::then 方法来计划在先行任务的值可用时打印该值的延续。
// basic-continuation.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>using namespace concurrency;
using namespace std;int wmain()
{auto t = create_task([]() -> int{return 42;});t.then([](int result){wcout << result << endl;}).wait();// Alternatively, you can chain the tasks directly and// eliminate the local variable./*create_task([]() -> int{return 42;}).then([](int result){wcout << result << endl;}).wait();*/
}/* Output:42
*/
可以按任意长度链接和嵌套任务。 一个任务还可以具有多个延续。 下面的示例演示将上一个任务的值增加三倍的基本延续链。
// continuation-chain.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>using namespace concurrency;
using namespace std;int wmain()
{auto t = create_task([]() -> int{ return 0;});// Create a lambda that increments its input value.auto increment = [](int n) { return n + 1; };// Run a chain of continuations and print the result.int result = t.then(increment).then(increment).then(increment).get();wcout << result << endl;
}/* Output:3
*/
延续还可以返回另一个任务。 如果没有取消,则此任务会在后续延续之前执行。 此技术称为异步解包。 要在后台执行其他工作,但不想当前任务阻止当前线程时,异步解包会很有用。 (这在 UWP 应用中很常见,其中延续可以在 UI 线程上运行)。 下面的示例演示三个任务。 第一个任务返回在延续任务之前运行的另一个任务。
// async-unwrapping.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>using namespace concurrency;
using namespace std;int wmain()
{auto t = create_task([](){wcout << L"Task A" << endl;// Create an inner task that runs before any continuation// of the outer task.return create_task([](){wcout << L"Task B" << endl;});});// Run and wait for a continuation of the outer task.t.then([](){wcout << L"Task C" << endl;}).wait();
}/* Output:Task ATask BTask C
*/
当任务的延续返回 N 类型的嵌套任务时,生成的任务具有 N 类型(而不是 task<N>),会在嵌套任务完成时完成。 换句话说,延续会执行嵌套任务的解包。
基于值的延续与基于任务的延续
对于其返回类型是 T 的 task 对象,可以向其延续任务提供 T 或 task<T> 类型的值。 采用类型 T 的延续称为基于值的延续。 基于值的延续计划在先行任务完成而未出现错误并且未取消时执行。 采用类型 task<T> 作为其参数的延续称为基于任务的延续。 基于任务的延续始终计划为在先行任务完成时执行,甚至是在先行任务取消或引发异常时执行。 随后然后调用 task::get 以获取先行任务的结果。 如果先行任务已取消,则 task::get 会引发 concurrency::task_canceled。 如果先行任务引发了异常,则 task::get 会再次引发该异常。 基于任务的延续在先行任务取消时不会标记为已取消。
when_all 函数
when_all 函数生成在任务集完成之后完成的任务。 此函数返回 std::vector 对象,其中包含集中每个任务的结果。 下面的基本示例使用 when_all 创建一个表示三个其他任务完成的任务。
// join-tasks.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>using namespace concurrency;
using namespace std;int wmain()
{// Start multiple tasks.array<task<void>, 3> tasks = {create_task([] { wcout << L"Hello from taskA." << endl; }),create_task([] { wcout << L"Hello from taskB." << endl; }),create_task([] { wcout << L"Hello from taskC." << endl; })};auto joinTask = when_all(begin(tasks), end(tasks));// Print a message from the joining thread.wcout << L"Hello from the joining thread." << endl;// Wait for the tasks to finish.joinTask.wait();
}/* Sample output:Hello from the joining thread.Hello from taskA.Hello from taskC.Hello from taskB.
*/
传递给 when_all 的任务必须统一。 换句话说,它们必须全部返回相同类型。
还可以使用 && 语法生成在任务集完成之后完成的任务,如下面的示例所示。
auto t = t1 && t2; // same as when_all
可将延续与 when_all 结合使用以在任务集完成之后执行操作,这十分常见。 下面的示例将上面的示例修改为打印各自生成 int 结果的三个任务的总和。
// Start multiple tasks.
array<task<int>, 3> tasks =
{create_task([]() -> int { return 88; }),create_task([]() -> int { return 42; }),create_task([]() -> int { return 99; })
};auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)
{wcout << L"The sum is " << accumulate(begin(results), end(results), 0)<< L'.' << endl;
});// Print a message from the joining thread.
wcout << L"Hello from the joining thread." << endl;// Wait for the tasks to finish.
joinTask.wait();/* Output:Hello from the joining thread.The sum is 229.
*/
在此示例中,还可以指定 task<vector<int>> 以生成基于任务的延续。
如果任务集中的任何任务取消或引发异常,则 when_all 会立即完成,不等待其余任务完成。 如果引发异常,则运行时会在你对 when_all 返回的任务对象调用 task::get 或 task::wait 时再次引发异常。 如果有多个任务引发,则运行时会选择其中之一。 因此,请确保在所有任务完成之后观察到所有异常;未经处理的任务异常会导致应用终止。
下面是可以用于确保程序观察到所有异常的实用工具函数。 对于处于提供的范围内的每个任务,observe_all_exceptions 会触发再次引发的任何异常,然后会吞并该异常。
// Observes all exceptions that occurred in all tasks in the given range.
template<class T, class InIt>
void observe_all_exceptions(InIt first, InIt last)
{std::for_each(first, last, [](concurrency::task<T> t){t.then([](concurrency::task<T> previousTask){try{previousTask.get();}// Although you could catch (...), this demonstrates how to catch specific exceptions. Your app// might handle different exception types in different ways.catch (Platform::Exception^){// Swallow the exception.}catch (const std::exception&){// Swallow the exception.}});});
}
请考虑一个使用 C++ 和 XAML 并将文件集写入磁盘的 UWP 应用。 下面的示例演示如何使用 when_all 和 observe_all_exceptions 确保该程序观察到所有异常。
// Writes content to files in the provided storage folder.
// The first element in each pair is the file name. The second element holds the file contents.
task<void> MainPage::WriteFilesAsync(StorageFolder^ folder, const vector<pair<String^, String^>>& fileContents)
{// For each file, create a task chain that creates the file and then writes content to it. Then add the task chain to a vector of tasks.vector<task<void>> tasks;for (auto fileContent : fileContents){auto fileName = fileContent.first;auto content = fileContent.second;// Create the file. The CreationCollisionOption::FailIfExists flag specifies to fail if the file already exists.tasks.emplace_back(create_task(folder->CreateFileAsync(fileName, CreationCollisionOption::FailIfExists)).then([content](StorageFile^ file){// Write its contents.return create_task(FileIO::WriteTextAsync(file, content));}));}// When all tasks finish, create a continuation task that observes any exceptions that occurred.return when_all(begin(tasks), end(tasks)).then([tasks](task<void> previousTask){task_status status = completed;try{status = previousTask.wait();}catch (COMException^ e){// We'll handle the specific errors below.}// TODO: If other exception types might happen, add catch handlers here.// Ensure that we observe all exceptions.observe_all_exceptions<void>(begin(tasks), end(tasks));// Cancel any continuations that occur after this task if any previous task was canceled.// Although cancellation is not part of this example, we recommend this pattern for cases that do.if (status == canceled){cancel_current_task();}});
}
下面是这个例子的运行:
1. 在 MainPage.xaml 中,添加一个 Button 控件。<Button x:Name="Button1" Click="Button_Click">Write files</Button>2. 在 MainPage.xaml.h 中,将这些前向声明添加到 MainPage 类声明的 private 节。void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
concurrency::task<void> WriteFilesAsync(Windows::Storage::StorageFolder^ folder, const std::vector<std::pair<Platform::String^, Platform::String^>>& fileContents);3. 在 MainPage.xaml.cpp 中,实现 Button_Click 事件处理程序。// A button click handler that demonstrates the scenario.
void MainPage::Button_Click(Object^ sender, RoutedEventArgs^ e)
{// In this example, the same file name is specified two times. WriteFilesAsync fails if one of the files already exists.vector<pair<String^, String^>> fileContents;fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 1")));fileContents.emplace_back(make_pair(ref new String(L"file2.txt"), ref new String(L"Contents of file 2")));fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 3")));Button1->IsEnabled = false; // Disable the button during the operation.WriteFilesAsync(ApplicationData::Current->TemporaryFolder, fileContents).then([this](task<void> previousTask){try{previousTask.get();}// Although cancellation is not part of this example, we recommend this pattern for cases that do.catch (const task_canceled&){// Your app might show a message to the user, or handle the error in some other way.}Button1->IsEnabled = true; // Enable the button.});
}4. 在 MainPage.xaml.cpp 中,实现 WriteFilesAsync,如示例所示。
when_all 是生成 task 作为其结果的的非阻止函数。 与 task::wait 不同,可以安全地在 UWP 应用中在 ASTA(应用程序 STA)线程上调用此函数。
相关文章:
windows C++-并行编程-PPL任务并行(二)
延续任务 在异步编程中,一个异步操作在完成时调用另一个操作并将数据传递到其中的情况非常常见。 传统上,这使用回调方法来完成。 在并发运行时中,延续任务提供了同样的功能。 延续任务(也简称为“延续”)是一个异步任务,由另一个…...
快速了解 servlet(SpringMVC 的底层)
Servlet 是 Java EE(现 Jakarta EE)中用于处理 Web 请求的核心组件。它在 Web 应用程序的服务器端运行,负责接收和处理客户端(如浏览器)的请求,并生成响应。 尽管现代Web开发更多采用SpringMVC等框架&…...
QT中tr的作用是什么
在Qt框架中,tr() 函数是一个非常重要的宏,它用于国际化和本地化(i18n和l10n)支持。tr() 函数使得Qt应用程序能够根据不同的语言环境(locale)显示相应的翻译文本,从而支持多种语言。 具体来说&a…...

OpenCV结构分析与形状描述符(7)计算轮廓的面积的函数contourArea()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 计算轮廓的面积。 该函数计算轮廓的面积。与 moments 类似,面积是使用格林公式计算的。因此,返回的面积与你使用 drawCo…...

内网环境使用Docker部署Qwen2模型-vLLM篇
在此之前,我们已成功利用Docker与Ollama框架,在内网环境中部署了Qwen2模型。下面我们再来看一下使用Docker与vLLM框架部署Qwen2模型。 准备vLLM镜像 在一台具备网络环境的机器上执行以下命令,拉取vLLM的镜像: # 官方镜像 docke…...

Rust的常数、作用域与所有权
【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学,之一 -CSDN博客 Rust到底值不值得学,之二-CSDN博客 Rust的数据类型-CSDN博客 3.7 常…...
Spring 源码解读:解决循环依赖的三种方式
引言 在复杂的应用开发中,循环依赖是一个常见的问题。简单来说,循环依赖是指两个或多个Bean之间互相依赖,导致程序无法正常实例化这些Bean。Spring容器通过依赖注入(DI)来管理Bean的创建与生命周期,并在遇…...

Web3 详解
1. 使用 Web3 库 Web3 是一个 JavaScript 库,可用于通过 RPC 通信与以太坊节点通信。 Web3 的工作方式是,公开已通过 RPC 启用的方法,这允许开发利用 Web3 库的用户界面,以便与部署在区块链上的合约进行交互。 一旦 Geth JavaScri…...
Spring 中依赖注入注解的区别详解
一、依赖注入的基本概念 依赖注入是一种设计模式,通过将对象的依赖以参数的形式传入类中,而不是在类中自行创建依赖对象。这样做有几个好处: 降低耦合度:类与类之间的依赖关系变得更清晰,避免了硬编码依赖。提高可测试性:通过依赖注入,可以轻松地进行单元测试,因为可以…...

PTA求一批整数中出现最多的个位数字
作者 徐镜春 单位 浙江大学 给定一批整数,分析每个整数的每一位数字,求出现次数最多的个位数字。例如给定3个整数1234、2345、3456,其中出现最多次数的数字是3和4,均出现了3次。 输入格式: 输入在第1行中给出正整数…...

探索国产编程工具:如何实现工作效率翻倍
在当前软件开发领域,国产编程工具正在迅速发展,它们在功能、性能以及用户体验上都有显著提升,以下是一些国产编程工具,它们可以帮助开发者提升工作效率。 智能代码编辑器 CodeGeeX:这是一款由清华大学和智谱AI合作开…...

秒懂:进程相关的操作
1.进程的查看 1.1创建test.cc文件,运行以下代码 #include <stdio.h> #include <sys/types.h> #include <unistd.h>int main() {while(1){sleep(1);} return 0;}1.2 执行以下命令 1. 运行test.cc文件 并将其最终的可执行文件命名为 test gcc t…...

PDF 软件如何帮助您编辑、转换和保护文件。
如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案,还是尝试组织和编辑主文档,PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时,请考虑这些因素。 1. 确定您的…...

蓝桥杯嵌入式国三备赛经验分享
1 学习STM32入门视频 向大家推荐一套宝藏级别的视频:【STM32入门教程-2023版 细致讲解 中文字幕】 如果已经比过蓝桥杯单片机或学习过单片机相关课程的同学,你们可以尝试不需要STM32套件进行学习。如果没有学过单片机相关课程的同学,可以买…...
AI编程工具合集
1. 简介 1.1. 概述 AI编程,即人工智能编程,是编写用于创建智能系统(如机器学习模型、自然语言处理应用程序等)的代码的过程。AI编程涉及使用算法和数据结构来实现能够执行任务的程序,这些任务通常需要人类智能才能完成。 AI编程的基础是计算机科学原理,包括数据结构、…...

[网络编程]通过java用TCP实现网络编程
文章目录 一. 通过java用TCP实现网络编程api介绍代码实现上述代码存在的问题 一. 通过java用TCP实现网络编程 api介绍 1. ServerSocket ServerSocket是专门给服务器用的api 构造方法: 方法: 2. Socket 不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后&#…...

Python(TensorFlow)和Java及C++受激发射损耗导图
🎯要点 神经网络监督去噪预测算法聚焦荧光团和检测模拟平台伪影消除算法性能优化方法自动化多尺度囊泡动力学成像生物研究多维分析统计物距粒子概率算法 Python和MATLAB图像降噪算法 消除噪声的一种方法是将原始图像与表示低通滤波器或平滑操作的掩模进行卷积。…...

IEEE投稿模板翻译
>将这一行替换为您的稿件id号(双击此处编辑)< IEEE 期刊和会议论文的撰写准备(2022) 第一作者 A. 作者,IEEE成员,第二作者 B. 作者,第三作者 C. 作者 Jr.,IEEE成员 摘要—本文档为IEEE会刊、期刊和…...
log4j 1.x 日志输出线程以唯一ID的形式配置
在 Log4j 1.x 中,直接以线程ID(如Java中的Thread.currentThread().getId()返回的ID)的形式记录日志是可行的,但 Log4j 1.x 本身并不直接提供一个内建的、自动将每个线程ID转换为“同一时间段内唯一ID”的机制。线程ID本身在JVM的上…...

宏观学习笔记:GDP分析(二)
GDP分析(一)主要是介绍GDP相关的定义以及核算逻辑,本节主要介绍GDP的分析思路。GDP分析主要是2种方法:总量分析和结构分析。 1. 总量分析 1.1 数值选择 一般情况下,分析的对象都是 官方公布的GDP当季值。 1.2 趋势规…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...