当前位置: 首页 > news >正文

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任务并行(二)

延续任务 在异步编程中&#xff0c;一个异步操作在完成时调用另一个操作并将数据传递到其中的情况非常常见。 传统上&#xff0c;这使用回调方法来完成。 在并发运行时中&#xff0c;延续任务提供了同样的功能。 延续任务(也简称为“延续”)是一个异步任务&#xff0c;由另一个…...

快速了解 servlet(SpringMVC 的底层)

Servlet 是 Java EE&#xff08;现 Jakarta EE&#xff09;中用于处理 Web 请求的核心组件。它在 Web 应用程序的服务器端运行&#xff0c;负责接收和处理客户端&#xff08;如浏览器&#xff09;的请求&#xff0c;并生成响应。 尽管现代Web开发更多采用SpringMVC等框架&…...

QT中tr的作用是什么

在Qt框架中&#xff0c;tr() 函数是一个非常重要的宏&#xff0c;它用于国际化和本地化&#xff08;i18n和l10n&#xff09;支持。tr() 函数使得Qt应用程序能够根据不同的语言环境&#xff08;locale&#xff09;显示相应的翻译文本&#xff0c;从而支持多种语言。 具体来说&a…...

OpenCV结构分析与形状描述符(7)计算轮廓的面积的函数contourArea()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算轮廓的面积。 该函数计算轮廓的面积。与 moments 类似&#xff0c;面积是使用格林公式计算的。因此&#xff0c;返回的面积与你使用 drawCo…...

内网环境使用Docker部署Qwen2模型-vLLM篇

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

Rust的常数、作用域与所有权

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学&#xff0c;之一 -CSDN博客 Rust到底值不值得学&#xff0c;之二-CSDN博客 Rust的数据类型-CSDN博客 3.7 常…...

Spring 源码解读:解决循环依赖的三种方式

引言 在复杂的应用开发中&#xff0c;循环依赖是一个常见的问题。简单来说&#xff0c;循环依赖是指两个或多个Bean之间互相依赖&#xff0c;导致程序无法正常实例化这些Bean。Spring容器通过依赖注入&#xff08;DI&#xff09;来管理Bean的创建与生命周期&#xff0c;并在遇…...

Web3 详解

1. 使用 Web3 库 Web3 是一个 JavaScript 库&#xff0c;可用于通过 RPC 通信与以太坊节点通信。 Web3 的工作方式是&#xff0c;公开已通过 RPC 启用的方法&#xff0c;这允许开发利用 Web3 库的用户界面&#xff0c;以便与部署在区块链上的合约进行交互。 一旦 Geth JavaScri…...

Spring 中依赖注入注解的区别详解

一、依赖注入的基本概念 依赖注入是一种设计模式,通过将对象的依赖以参数的形式传入类中,而不是在类中自行创建依赖对象。这样做有几个好处: 降低耦合度:类与类之间的依赖关系变得更清晰,避免了硬编码依赖。提高可测试性:通过依赖注入,可以轻松地进行单元测试,因为可以…...

PTA求一批整数中出现最多的个位数字

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

探索国产编程工具:如何实现工作效率翻倍

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

秒懂:进程相关的操作

1.进程的查看 1.1创建test.cc文件&#xff0c;运行以下代码 #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 解决方案&#xff0c;还是尝试组织和编辑主文档&#xff0c;PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时&#xff0c;请考虑这些因素。 1. 确定您的…...

蓝桥杯嵌入式国三备赛经验分享

1 学习STM32入门视频 向大家推荐一套宝藏级别的视频&#xff1a;【STM32入门教程-2023版 细致讲解 中文字幕】 如果已经比过蓝桥杯单片机或学习过单片机相关课程的同学&#xff0c;你们可以尝试不需要STM32套件进行学习。如果没有学过单片机相关课程的同学&#xff0c;可以买…...

AI编程工具合集

1. 简介 1.1. 概述 AI编程,即人工智能编程,是编写用于创建智能系统(如机器学习模型、自然语言处理应用程序等)的代码的过程。AI编程涉及使用算法和数据结构来实现能够执行任务的程序,这些任务通常需要人类智能才能完成。 AI编程的基础是计算机科学原理,包括数据结构、…...

[网络编程]通过java用TCP实现网络编程

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

Python(TensorFlow)和Java及C++受激发射损耗导图

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

IEEE投稿模板翻译

>将这一行替换为您的稿件id号(双击此处编辑)< IEEE 期刊和会议论文的撰写准备&#xff08;2022&#xff09; 第一作者 A. 作者&#xff0c;IEEE成员&#xff0c;第二作者 B. 作者&#xff0c;第三作者 C. 作者 Jr.&#xff0c;IEEE成员 摘要—本文档为IEEE会刊、期刊和…...

log4j 1.x 日志输出线程以唯一ID的形式配置

在 Log4j 1.x 中&#xff0c;直接以线程ID&#xff08;如Java中的Thread.currentThread().getId()返回的ID&#xff09;的形式记录日志是可行的&#xff0c;但 Log4j 1.x 本身并不直接提供一个内建的、自动将每个线程ID转换为“同一时间段内唯一ID”的机制。线程ID本身在JVM的上…...

宏观学习笔记:GDP分析(二)

GDP分析&#xff08;一&#xff09;主要是介绍GDP相关的定义以及核算逻辑&#xff0c;本节主要介绍GDP的分析思路。GDP分析主要是2种方法&#xff1a;总量分析和结构分析。 1. 总量分析 1.1 数值选择 一般情况下&#xff0c;分析的对象都是 官方公布的GDP当季值。 1.2 趋势规…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...