windows C++ 并行编程-并发和UWP(三)

2024-09-04 07:12

本文主要是介绍windows C++ 并行编程-并发和UWP(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

控制执行线程

Windows 运行时使用 COM 线程模型。 在此模型中,根据对象处理其同步的方式,对象被托管在不同的单元中。 线程安全对象托管在多线程单元 (MTA) 中。 必须通过单个线程访问的对象托管在单线程单元 (STA) 中。

在具有 UI 的应用程序中,ASTA(应用程序 STA)线程负责发送窗口消息而且它是进程中唯一可以更新 STA 托管的 UI 控件的线程。 这会产生两种后果。 第一种是,要使应用程序保持响应状态,所有占用大量 CPU 的操作和 I/O 操作都不应在 ASTA 线程上运行。 第二种是,来自后台线程的结果都必须封送回 ASTA 以更新 UI。 在 C++ UWP 应用中,MainPage 和其他 XAML 页面都在 ATSA 中运行。 因此,在 ASTA 中声明的任务延续默认情况下也会在此运行,因此您可以在延续主体中直接更新控件。 但是,如果在另一个任务中嵌套任务,则此嵌套任务中的任何延续都在 MTA 中运行。 因此,您需要考虑是否显式指定这些延续在什么上下文中运行。

从异步操作创建的任务(如 IAsyncOperation<TResult>),使用了特殊语义,可以帮助您忽略线程处理详细信息。 虽然操作可能会在后台线程上运行(或者它可能根本不由线程支持),但其延续在默认情况下一定会在启动了延续操作的单元上运行(换言之,从调用了 task::then的单元运行)。 可以使用 concurrency::task_continuation_context 类来控制延续的执行上下文。 使用这些静态帮助器方法来创建 task_continuation_context 对象:

  • 使用 concurrency::task_continuation_context::use_arbitrary 指定延续在后台线程上运行;
  • 使用 concurrency::task_continuation_context::use_current 指定延续在调用了 task::then的线程上运行;

可以将 task_continuation_context 对象传递给 task::then 方法以显式控制延续的执行上下文,或者可以将任务传递给另一单元,然后调用 task::then 方法以隐式控制执行上下文。

由于 UWP 应用的主 UI 线程在 STA 下运行,因此在该 STA 中创建的延续默认情况下在 STA 中运行。 相应地,在 MTA 中创建的延续将在 MTA 中运行。

下面一节介绍一种应用程序,该应用程序从磁盘读取一个文件,查找该文件中最常见的单词,然后在 UI 中显示结果。 最终操作(更新 UI)将在 UI 线程上发生。

此行为特定于 UWP 应用。 对于桌面应用程序,您无法控制延续的运行位置。 相反,计划程序会选择要运行每个延续的辅助线程。

对于在 STA 中运行的延续的主体,请不要调用 concurrency::task::wait 。 否则,运行时会引发 concurrency::invalid_operation ,原因是此方法阻止当前线程并可能导致应用停止响应。 但是,你可以调用 concurrency::task::get 方法来接收基于任务的延续中的先行任务的结果。

示例:使用 C++ 和 XAML 在 Windows 运行时应用中控制执行

假设有一个 C++ XAML 应用程序,该应用程序从磁盘读取一个文件,在该文件中查找最常见的单词,然后在 UI 中显示结果。 若要创建此应用,请首先在 Visual Studio 中创建“空白应用(通用 Windows)”项目并将其命名为 CommonWords。 在应用程序清单中,指定“文档库” 功能以使应用程序能够访问“文档”文件夹。 同时将文本 (.txt) 文件类型添加到应用程序清单的声明部分。 有关应用功能和声明的详细信息,请参阅 Windows 应用的打包、部署和查询。

更新 MainPage.xaml 中的 Grid 元素,以包含 ProgressRing 元素和 TextBlock 元素。 ProgressRing 指示操作正在进行, TextBlock 显示计算的结果。

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"><ProgressRing x:Name="Progress"/><TextBlock x:Name="Results" FontSize="16"/>
</Grid>

将以下 #include 语句添加到 pch.h。

#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>

将以下方法声明添加到 MainPage 类 (MainPage.h)。

private:// Splits the provided text string into individual words.concurrency::task<std::vector<std::wstring>> MakeWordList(Platform::String^ text);// Finds the most common words that are at least the provided minimum length.concurrency::task<std::vector<std::pair<std::wstring, size_t>>> FindCommonWords(const std::vector<std::wstring>& words, size_t min_length, size_t count);// Shows the most common words on the UI.void ShowResults(const std::vector<std::pair<std::wstring, size_t>>& commonWords);

将以下 using 语句添加到 MainPage.cpp。

using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;

在 MainPage.cpp 中,实现 MainPage::MakeWordList、 MainPage::FindCommonWords和 MainPage::ShowResults 方法。 MainPage::MakeWordList 和 MainPage::FindCommonWords 执行计算密集型操作。 MainPage::ShowResults 方法在 UI 中显示计算的结果。

// Splits the provided text string into individual words.
task<vector<wstring>> MainPage::MakeWordList(String^ text)
{return create_task([text]() -> vector<wstring>{vector<wstring> words;// Add continuous sequences of alphanumeric characters to the string vector.wstring current_word;for (wchar_t ch : text){if (!iswalnum(ch)){if (current_word.length() > 0){words.push_back(current_word);current_word.clear();}}else{current_word += ch;}}return words;});
}// Finds the most common words that are at least the provided minimum length.
task<vector<pair<wstring, size_t>>> MainPage::FindCommonWords(const vector<wstring>& words, size_t min_length, size_t count)
{return create_task([words, min_length, count]() -> vector<pair<wstring, size_t>>{typedef pair<wstring, size_t> pair;// Counts the occurrences of each word.concurrent_unordered_map<wstring, size_t> counts;parallel_for_each(begin(words), end(words), [&counts, min_length](const wstring& word){// Increment the count of words that are at least the minimum length. if (word.length() >= min_length){// Increment the count.InterlockedIncrement(&counts[word]);}});// Copy the contents of the map to a vector and sort the vector by the number of occurrences of each word.vector<pair> wordvector;copy(begin(counts), end(counts), back_inserter(wordvector));sort(begin(wordvector), end(wordvector), [](const pair& x, const pair& y){return x.second > y.second;});size_t size = min(wordvector.size(), count);wordvector.erase(begin(wordvector) + size, end(wordvector));return wordvector;});
}// Shows the most common words on the UI. 
void MainPage::ShowResults(const vector<pair<wstring, size_t>>& commonWords)
{wstringstream ss;ss << "The most common words that have five or more letters are:";for (auto commonWord : commonWords){ss << endl << commonWord.first << L" (" << commonWord.second << L')';}// Update the UI.Results->Text = ref new String(ss.str().c_str());
}

修改 MainPage 构造函数,以创建一个在 UI 中显示荷马的 伊利亚特 一书中常见单词的延续任务链。 前两个延续任务会将文本拆分为单个词并查找常见词,这会非常耗时,因此将其显式设置为在后台运行。 最终延续任务(即更新 UI)不指定延续上下文,因此遵循单元线程处理规则。

MainPage::MainPage()
{InitializeComponent();// To run this example, save the contents of http://www.gutenberg.org/files/6130/6130-0.txt to your Documents folder.// Name the file "The Iliad.txt" and save it under UTF-8 encoding.// Enable the progress ring.Progress->IsActive = true;// Find the most common words in the book "The Iliad".// Get the file.create_task(KnownFolders::DocumentsLibrary->GetFileAsync("The Iliad.txt")).then([](StorageFile^ file){// Read the file text.return FileIO::ReadTextAsync(file, UnicodeEncoding::Utf8);// By default, all continuations from a Windows Runtime async operation run on the // thread that calls task.then. Specify use_arbitrary to run this continuation // on a background thread.}, task_continuation_context::use_arbitrary()).then([this](String^ file){// Create a word list from the text.return MakeWordList(file);// By default, all continuations from a Windows Runtime async operation run on the // thread that calls task.then. Specify use_arbitrary to run this continuation // on a background thread.}, task_continuation_context::use_arbitrary()).then([this](vector<wstring> words){// Find the most common words.return FindCommonWords(words, 5, 9);// By default, all continuations from a Windows Runtime async operation run on the // thread that calls task.then. Specify use_arbitrary to run this continuation // on a background thread.}, task_continuation_context::use_arbitrary()).then([this](vector<pair<wstring, size_t>> commonWords){// Stop the progress ring.Progress->IsActive = false;// Show the results.ShowResults(commonWords);// We don't specify a continuation context here because we want the continuation // to run on the STA thread.});
}

此示例演示了如何指定执行上下文以及如何构成延续链。 回想一下,从异步操作创建的任务默认情况下在调用了 task::then的单元上运行其延续。 因此,此示例使用 task_continuation_context::use_arbitrary 来指定不涉及 UI 的操作在后台线程上执行。 

 下图显示 CommonWords 应用的结果。

在此示例中,可以支持取消操作,因为支持 create_async 的 task 对象使用了隐式取消标记。 如果您的任务需要以协作方式响应取消,则请定义您的工作函数以采用 cancellation_token 对象。

这篇关于windows C++ 并行编程-并发和UWP(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135344

相关文章

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

Windows设置nginx启动端口的方法

《Windows设置nginx启动端口的方法》在服务器配置与开发过程中,nginx作为一款高效的HTTP和反向代理服务器,被广泛应用,而在Windows系统中,合理设置nginx的启动端口,是确保其正... 目录一、为什么要设置 nginx 启动端口二、设置步骤三、常见问题及解决一、为什么要设置 nginx

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C