在 C++ 编程领域,多线程开辟一直是一项具有挑战性的任务,尤其是在确保线程安全和有效管理线程生命周期方面。随着 C++20 的发布,线程管理和取消机制迎来了重大革新,其中 std::jthread、 std::stop_token、 std::stop_source 和 std::stop_callback 成为了这一改进的核心要素。这些新特性不仅极大地提升了线程管理的安全性和便捷性,还明显增强了线程协作取消的效率与可靠性,为开辟者构建高性能、妥当的多线程应用程序提供了强有力的支持。本文将深入探讨这些新特性的底层原理、详细用法以及在现实开辟场景中的详细应用。
std::jthread:更智能的线程管理
背景与上风
在传统的 C++ 多线程编程中,std::thread 作为线程管理的基础工具,要求开辟者手动调用 join 方法来等候线程执行完毕,以避免资源泄漏。然而,手动管理线程的生命周期容易堕落,一旦忘记调用 join,就大概导致线程资源无法正确释放,进而引发各种难以调试的问题。
std::jthread 正是为了解决这些痛点而引入的。它作为 std::thread 的增强版本,最大的亮点在于其自动加入(join)机制。当 std::jthread 对象离开其作用域时,它会自动调用 join 方法,确保线程资源得到妥善管理,无需开辟者手动干预,从而大大降低了因线程生命周期管理不当而导致的错误风险。
构造函数与 std::stop_token 的集成
std::jthread 的构造函数设计十分灵活,它允许开辟者传递一个函数及其相关参数。特别值得注意的是,如果传递的函数接受 std::stop_token 作为其首参数,那么在新线程开始执行时,会自动将该令牌传递给函数。这种设计为线程提供了一种便捷的机制,使其能够在执行过程中随时查抄是否收到了取消请求,从而实现线程的可停止执行。
例如,假设有一个复杂的计算任务函数 computeTask,它大概需要耗费较长时间来完成。我们可以将其界说为接受 std::stop_token 作为参数:
- void computeTask(std::stop_token stopToken) {
- // 复杂的计算逻辑
- while (true) {
- // 执行计算步骤
- if (stopToken.stop_requested()) {
- // 处理取消请求
- break;
- }
- }
- }
复制代码 然后,通过 std::jthread 来启动这个任务:
- std::jthread taskThread(computeTask, std::stop_token());
复制代码 这样,在任务执行过程中,如果有其他部分的代码发出了取消请求,computeTask 函数能够及时检测到并做出相应的处理,确保线程能够安全、有序地退出。
std::stop_token、std::stop_source 和 std::stop_callback:灵活的取消机制
std::stop_token:取消请求的指示器
std::stop_token 本质上是一个摆列类型,它饰演着取消请求指示器的角色。线程可以通过查询 std::stop_token 的状态,来判断是否收到了取消请求。当与 std::stop_source 共同使用时,std::stop_token 的作用更加明显。
std::stop_source:取消请求的发起者
std::stop_source 是一个专门用于发出取消请求的对象。它就像是一个控制中心,一旦调用其 request_stop 方法,所有与之关联的 std::stop_token 都会吸收到取消通知。这种一对多的关联机制,使得在多线程环境中,能够方便地统一管理多个线程的取消操作。
例如,在一个多线程的数据处理体系中,大概有多个线程同时在处理差别的数据块。我们可以创建一个 std::stop_source 对象,并将其关联的 std::stop_token 传递给每个处理线程。当需要停止整个数据处理过程时,只需调用 std::stop_source 的 request_stop 方法,所有相关线程就能立即感知到并做出相应的处理。
std::stop_callback:取消时的自界说处理
std::stop_callback 为线程在收到取消请求时执行自界说清算工作或其他须要操作提供了便利。它是一个可以注册到 std::stop_token 上的回调函数。当关联的 std::stop_source 发出取消请求时,所有注册在相应 std::stop_token 上的回调函数都会被依次调用。
例如,在一个涉及文件操作的多线程应用中,某个线程大概在执行过程中打开了多个文件进行读写操作。当该线程收到取消请求时,我们可以通过注册 std::stop_callback 来确保所有打开的文件被正确关闭,避免资源泄漏和数据差别等问题:
- std::stop_source fileOpSource;
- std::stop_token fileOpToken = fileOpSource.get_token();
- std::stop_callback callback(fileOpToken, [] {
- // 关闭所有打开的文件
- // 释放相关资源
- });
复制代码 通过这种方式,我们能够在取消请求发生时,对线程的资源进行准确管理,保证程序的健壮性和稳定性。
使用示例
简朴的线程取消示例
下面通过一个详细的代码示例来展示 std::jthread、std::stop_token、std::stop_source 和 std::stop_callback 的基本用法:
- #include <iostream>
- #include <stop_token>
- #include <thread>
- #include <vector>
- void worker(int id, std::stop_token stopToken) {
- for (int i = 0; i < 10; ++i) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (stopToken.stop_requested()) {
- std::cout << "Worker " << id << " is requested to stop." << std::endl;
- return;
- }
- std::cout << "Worker " << id << " is running." << std::endl;
- }
- }
- int main() {
- std::stop_source stopSource;
- std::jthread thread1(worker, 1, stopSource.get_token());
- std::jthread thread2(worker, 2, stopSource.get_token());
- std::this_thread::sleep_for(std::chrono::seconds(1));
- stopSource.request_stop();
- std::cout << "Request stop." << std::endl;
- return 0;
- }
复制代码 在这个示例中,我们界说了一个 worker 函数,它接受一个线程 ID 和一个 std::stop_token。在 worker 函数内部,通过循环模仿线程的工作过程,并在每次循环中查抄 std::stop_token 的状态。如果收到取消请求,线程将打印相应的提示信息并退出。
在 main 函数中,我们创建了一个 std::stop_source 对象,并通过它获取两个 std::stop_token,分别传递给两个 std::jthread 对象。主线程等候一秒钟后,调用 std::stop_source 的 request_stop 方法发出取消请求。此时,两个工作线程会检测到 std::stop_token 的状态变化,从而安全地退出。
多任务的协作取消示例
在现实应用中,经常会碰到多个并发任务需要协同取消的场景。以下是一个更具现实意义的示例,展示了怎样通过共享 std::stop_source 实现多任务的协作取消:
- #include <chrono>
- #include <iostream>
- #include <stop_token>
- #include <syncstream>
- #include <thread>
- #include <vector>
- using namespace std::chrono_literals;
- void download_task(std::stop_token token, int id) {
- int progress = 0;
- while (progress < 100) {
- if (token.stop_requested()) {
- std::osyncstream(std::cout)
- << "[任务 " << id << "] 检测到取消请求, 停止下载.\n";
- return;
- }
- std::this_thread::sleep_for(200ms);
- progress += 10;
- std::osyncstream(std::cout)
- << "[任务 " << id << "] 当前进度: " << progress << "%\n";
- }
- std::osyncstream(std::cout) << "[任务 " << id << "] 下载完成!\n";
- }
- int main() {
- // 控制多个线程的退出
- std::stop_source source;
- std::vector<std::jthread> tasks;
- for (int i = 0; i < 3; i++) {
- tasks.emplace_back(download_task, source.get_token(), i);
- }
- std::this_thread::sleep_for(1s);
- std::cout << "[主线程] 取消所有任务.\n";
- source.request_stop(); // 发出取消信号
- return 0;
- }
复制代码 在这个示例中,我们界说了一个 download_task 函数,模仿一个下载任务。每个下载任务在执行过程中会定期查抄 std::stop_token 的状态。如果收到取消请求,任务将打印提示信息并停止下载。
在 main 函数中,我们创建了一个 std::stop_source 对象,并使用它启动了三个 std::jthread,每个线程执行一个 download_task。主线程等候一秒钟后,调用 std::stop_source 的 request_stop 方法,向所有下载任务发出取消请求。所有下载任务在检测到取消请求后,会安全地退出,实现了多任务的协作取消。
结论
C++20 引入的 std::jthread、std::stop_token、std::stop_source 和 std::stop_callback 为线程管理和取消带来了革命性的变化。这些新特性构建了一个更加安全、灵活的线程管理体系,使得开辟者能够以更高效、更可靠的方式编写多线程程序。通过公道运用这些特性,我们能够有效避免传统多线程编程中常见的资源泄漏、线程安全等问题,构建出更加健壮、稳定且高性能的并发应用程序。无论是在开辟大型服务器端应用、图形处理软件,还是在进行科学计算等领域,这些新的线程管理和取消机制都将发挥重要作用,助力开辟者提升程序的质量和性能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |