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

相关文章

Windows 上如果忘记了 MySQL 密码 重置密码的两种方法

《Windows上如果忘记了MySQL密码重置密码的两种方法》:本文主要介绍Windows上如果忘记了MySQL密码重置密码的两种方法,本文通过两种方法结合实例代码给大家介绍的非常详细,感... 目录方法 1:以跳过权限验证模式启动 mysql 并重置密码方法 2:使用 my.ini 文件的临时配置在 Wi

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

Redis在windows环境下如何启动

《Redis在windows环境下如何启动》:本文主要介绍Redis在windows环境下如何启动的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis在Windows环境下启动1.在redis的安装目录下2.输入·redis-server.exe

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元