【知识点】std::thread::detach std::lock_guard std::unique_lock

在 C++11 中,std::thread 提供了并发编程的基础设施,使得我们可以创建和管理线程。std::threaddetach 方法是一种常用的线程管理方式,允许线程在后台独立运行,而不必与主线程同步或等待其完成。

std::thread::detach 方法

当你调用 std::thread 对象的 detach 方法时,线程将与其创建的 std::thread 对象分离,允许它在后台继续运行。当主线程(或其他线程)不再需要等待线程完成时,可以使用 detach 方法。

调用 detach 方法后,std::thread 对象将不再与创建的线程关联,并且无法使用 join 方法来等待线程结束。如果 std::thread 对象在未调用 detachjoin 的情况下被销毁,会导致程序终止。

示例

下面是一个使用 std::thread::detach 方法的示例:

#include <iostream>
#include <thread>
#include <chrono>void backgroundTask() {for (int i = 0; i < 5; ++i) {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Background task running: " << i + 1 << " seconds\n";}std::cout << "Background task completed.\n";
}int main() {std::thread t(backgroundTask);  // 创建一个线程运行 backgroundTask 函数t.detach();  // 分离线程std::cout << "Main thread continues to run...\n";std::this_thread::sleep_for(std::chrono::seconds(3));std::cout << "Main thread completed.\n";return 0;
}

在这个示例中,backgroundTask 函数将在后台运行 5 秒,每秒打印一次消息。当主线程调用 t.detach() 分离线程后,它继续执行其自身的操作,不再等待 backgroundTask 完成。

注意事项

  1. 无法使用 join:一旦线程被分离,就不能再使用 join 方法来等待它完成。这意味着主线程无法确保分离的线程何时完成或是否成功完成。

  2. 线程资源管理:分离线程在后台运行,直到它完成或程序结束。确保分离的线程在程序结束前完成,以避免可能的资源泄漏或未定义行为。

  3. 适用场景detach 适用于那些不需要同步或等待的后台任务,例如日志记录、定时任务或其他非关键任务。

  4. 确保线程完成:当主线程退出时,分离的线程可能仍在运行。为了确保所有分离线程在程序结束前完成,可以使用适当的同步机制,如条件变量或其他同步手段。

进一步的示例

以下示例展示了如何使用条件变量来确保所有分离的线程在主线程完成前都已完成:

#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool done = false;void backgroundTask(int id) {std::this_thread::sleep_for(std::chrono::seconds(id));std::cout << "Thread " << id << " completed.\n";std::unique_lock<std::mutex> lock(mtx);done = true;cv.notify_all();
}int main() {std::vector<std::thread> threads;for (int i = 1; i <= 5; ++i) {std::thread t(backgroundTask, i);t.detach();}std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return done; });std::cout << "All threads completed.\n";return 0;
}

在这个示例中,使用条件变量 cv 和互斥锁 mtx 来确保所有分离的线程在主线程完成前都已完成。当一个分离线程完成其任务时,它通知主线程更新状态。

总结

std::thread::detach 方法使得线程在后台独立运行,主线程无需等待其完成。这种方法适用于不需要同步或等待的后台任务,但需要注意管理线程的生命周期和资源,以确保程序正常运行。

std::lock_guard 是 C++11 引入的一个便利类,用于在作用域内自动管理互斥锁的锁定和解锁。它主要用于确保在函数或代码块执行期间互斥锁被正确地锁定和解锁,即使在异常或提前返回的情况下,也能够确保互斥锁被正确释放。

std::lock_guard

基本用法

std::lock_guard 的主要目的是提供一种简单的方式来确保互斥锁在作用域结束时自动解锁。它通过 RAII(资源获取即初始化)机制来实现这一点。

示例
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::lock_guard<std::mutex> lock(mtx);  // 锁定互斥锁std::cout << "Thread ID: " << id << std::endl;// 在作用域结束时,互斥锁会自动解锁
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,std::lock_guard 确保在 print_thread_id 函数内互斥锁 mtx 被锁定,并在函数结束时自动解锁。

工作原理

std::lock_guard 的构造函数接受一个互斥锁对象,并在构造时锁定互斥锁。它的析构函数会自动解锁互斥锁,因此,当 std::lock_guard 对象超出其作用域时,互斥锁会自动解锁。

template <typename Mutex>
class lock_guard {
public:explicit lock_guard(Mutex& m) : mutex(m) {mutex.lock();}~lock_guard() {mutex.unlock();}lock_guard(const lock_guard&) = delete;lock_guard& operator=(const lock_guard&) = delete;private:Mutex& mutex;
};

使用场景

std::lock_guard 适用于简单的锁定场景,不需要手动管理锁的生命周期。它在以下情况下非常有用:

  1. 保护临界区:确保在多线程环境中对共享资源的访问是安全的。
  2. 异常安全:即使在函数中抛出异常,std::lock_guard 也能确保互斥锁被正确解锁。

std::unique_lock 的比较

std::unique_lock 提供了更灵活的锁管理功能,例如延迟锁定、提前解锁、再次锁定等。相比之下,std::lock_guard 更简单,但功能也更有限。

std::unique_lock 示例
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx);  // 锁定互斥锁std::cout << "Thread ID: " << id << std::endl;// 可选:提前解锁lock.unlock();// 可选:再次锁定lock.lock();// 在作用域结束时,互斥锁会自动解锁
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,std::unique_lock 提供了更多的灵活性,可以提前解锁和再次锁定互斥锁。

总结

  • std::lock_guard 是一个简单的 RAII 机制,用于在作用域内自动管理互斥锁的锁定和解锁。
  • 它适用于简单的锁定场景,确保在异常或提前返回的情况下互斥锁被正确释放。
  • 对于需要更灵活锁管理的情况,可以使用 std::unique_lock

std::unique_lock

std::unique_lock 是 C++11 引入的一个模板类,用于管理互斥锁的锁定和解锁。与 std::lock_guard 相比,std::unique_lock 提供了更强大的功能和更灵活的锁管理选项。它允许延迟锁定、提前解锁、再次锁定等操作,是一种更通用的互斥锁管理方式。

std::unique_lock 的功能和用法

基本用法

std::unique_lock 的基本用法与 std::lock_guard 类似,但它提供了更多的功能。以下是一个基本示例:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx);  // 锁定互斥锁std::cout << "Thread ID: " << id << std::endl;// 在作用域结束时,互斥锁会自动解锁
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,std::unique_lock 确保在 print_thread_id 函数内互斥锁 mtx 被锁定,并在函数结束时自动解锁。

延迟锁定

std::unique_lock 允许延迟锁定互斥锁,即在创建 std::unique_lock 对象时不立即锁定互斥锁。可以使用 defer_lock 标签来实现这一点:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 不立即锁定互斥锁// 需要时手动锁定lock.lock();std::cout << "Thread ID: " << id << std::endl;// 自动解锁
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,互斥锁在创建 std::unique_lock 对象时并未被锁定,而是在需要时手动锁定。

提前解锁

可以使用 unlock 方法提前解锁互斥锁:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx);  // 锁定互斥锁std::cout << "Thread ID: " << id << std::endl;lock.unlock();  // 提前解锁// 其他不需要锁定的操作
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,互斥锁在 print_thread_id 函数内提前解锁,以便进行其他不需要锁定的操作。

试图锁定

可以使用 try_lock 方法尝试锁定互斥锁而不阻塞:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_thread_id(int id) {std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);  // 尝试锁定互斥锁if (lock.owns_lock()) {std::cout << "Thread " << id << " successfully locked the mutex." << std::endl;} else {std::cout << "Thread " << id << " failed to lock the mutex." << std::endl;}
}int main() {std::thread t1(print_thread_id, 1);std::thread t2(print_thread_id, 2);t1.join();t2.join();return 0;
}

在这个示例中,try_lock 方法尝试锁定互斥锁。如果锁定成功,owns_lock 方法将返回 true;否则返回 false

std::unique_lock 的构造函数

std::unique_lock 有多个构造函数,可以用于不同的锁定策略:

  • 默认构造:不锁定互斥锁。
  • 立即锁定构造:在创建时立即锁定互斥锁。
  • 延迟锁定构造:在创建时不锁定互斥锁,可以稍后手动锁定。
  • 尝试锁定构造:尝试锁定互斥锁。

详细示例

下面是一个更详细的示例,展示了如何使用 std::unique_lock 进行延迟锁定、提前解锁和尝试锁定:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void worker(int id) {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 延迟锁定std::cout << "Thread " << id << " is waiting to lock the mutex..." << std::endl;// 尝试锁定if (lock.try_lock()) {std::cout << "Thread " << id << " has locked the mutex." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));lock.unlock();std::cout << "Thread " << id << " has unlocked the mutex." << std::endl;} else {std::cout << "Thread " << id << " failed to lock the mutex." << std::endl;}
}int main() {std::thread t1(worker, 1);std::thread t2(worker, 2);t1.join();t2.join();return 0;
}

在这个示例中,两个线程都尝试锁定同一个互斥锁。使用 std::unique_lock 的延迟锁定和尝试锁定机制,线程可以在失败时进行不同的处理。

总结

  • std::unique_lock 提供了更灵活的锁管理功能,包括延迟锁定、提前解锁和尝试锁定。
  • 它比 std::lock_guard 更强大,但也更复杂,适用于需要灵活锁管理的场景。
  • 通过理解 std::unique_lock 的使用,可以更好地管理多线程环境下的资源同步,编写更安全和高效的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/28300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode最详尽解答】125-验证回文串 Valid-Palindrome

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家&#xff01; 链接: 125-验证回文串 直觉 这个问题需要使用一些内置函数&#xff0c;比如 s[l].isalnum() 和 s[l].…

springboot整合security

整合Spring Security到Spring Boot项目中可以帮助你实现认证&#xff08;Authentication&#xff09;和授权&#xff08;Authorization&#xff09;&#xff0c;从而保护你的应用程序资源和数据。下面是一个基本的步骤指南&#xff0c;帮助你在Spring Boot项目中整合和配置Spri…

Docker运行 Redis、Mysql、Nginx、MongoDB、Minio等

Redis 挂载文件&#xff0c;自行选择 wget http://download.redis.io/redis-stable/redis.conf 创建对应的文件 mkdir -p /docker/redis/data mkdir -p /docker/redis/conf touch /docker/redis/conf/redis.conf # redis.conf什么的都不能是空docker pull redis:6.0.8dock…

Web前端真实简历:深入解析关键要点与技巧

Web前端真实简历&#xff1a;深入解析关键要点与技巧 在数字化快速发展的今天&#xff0c;Web前端技术已成为互联网行业的核心领域之一。一份真实而引人注目的Web前端简历&#xff0c;对于求职者来说至关重要。本文将从四个方面、五个方面、六个方面和七个方面&#xff0c;深入…

C++ 55 之 多继承

#include <iostream> #include <string> using namespace std;class Base08_1{ public:int m_a;Base08_1(){this->m_a 10;} };class Base08_2{ public:// int m_b;int m_a;Base08_2(){// this->m_b 20;this->m_a 30;} };// 多继承 继承的类型都要…

GenICam标准(一)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; GenICam标准&#xff08;四&#xff09; GenICam标准&#xff08;五&#xff09; GenICam标准&#xff08;六&#xff09; 文章目录 系列文…

非对称加密系统解析

目录 1. 概述 2. 非对称加密标准 2.1 RSA 2.2 SM2 2.2.1 SM2私钥 2.2.2 SM2公钥 2.2.3 加密数据格式 2.2.4 签名数据格式 1. 概述 非对称加密中&#xff0c;密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密&#xff0c;接收者用解密密钥对密文进行解密…

Zookeeper:客户端命令行操作

文章目录 一、help二、ls path三、create四、get path五、set六、stat七、delete八、deleteall 一、help 显示所有操作命令。 二、ls path 使用ls命令来查看当前znode的子节点[可监听] w&#xff1a;监听子节点变化。s&#xff1a;附加次级信息。 三、create 普通创建&am…

DOM的概念?获取html元素的方法有哪些?

Dom文档对象模型&#xff0c;对js的HTML具有操作能力。 获取HTML的方法&#xff1a; Document.getElementById(‘’) 通过元素 id 来查找元素&#xff1b; Document.getElementsByClassName(‘’) 通过类名来查找元素; Document.getElementsByName(‘’) 通过表单元素中 na…

element--el-table添加合计后固定列x轴滚动条无法滚动问题

效果图 改变固定列滚轮高度问题 解决文章 解决方案 使用到的参数 pointer-events 属性用来控制一个元素能否响应鼠标操作&#xff0c;常用的关键字有 auto 和 none pointer-events: none; 让一个元素忽略鼠标操作 pointer-events: auto; 还原浏览器设定的默认行为 代码演示 添…

web前端网上私活:探索、挑战与成长的独特之旅

web前端网上私活&#xff1a;探索、挑战与成长的独特之旅 在这个数字化飞速发展的时代&#xff0c;Web前端网上私活成为了越来越多开发者的选择。它不仅仅是一种获取额外收入的方式&#xff0c;更是一种挑战自我、提升技能、拓宽视野的独特旅程。接下来&#xff0c;我将从四个…

JVM-GC-基础知识

JVM-GC-基础知识 前言 JVM中的GC使用Root Searching寻找垃圾单独方式&#xff0c;并结合mark-sweep、copying、mark-compact三种清除算法形成了各有特点的垃圾回收器&#xff0c;且垃圾回收器的演变过程是因为内存空间的不断增大的必然原因。 JVM-GC的发展史 JVM垃圾回收分…

解释时间复杂度和空间复杂度的概念

在算法和数据结构的学习中&#xff0c;时间复杂度和空间复杂度是两个至关重要的概念。它们用于衡量算法在执行过程中所需要的时间和空间资源。下面我将从技术难点、面试官关注点、回答吸引力以及代码举例四个方面来详细解释这两个概念。 一、技术难点 时间复杂度 定义与理解…

Python文本处理:初探《三国演义》

Python文本处理&#xff1a;初探《三国演义》 三国演义获取文本文本预处理分词与词频统计引入停用词后进行词频统计分析人物出场次数结果可视化完整代码 三国演义 《三国演义》是中国古代四大名著之一&#xff0c;它以东汉末年到晋朝统一之间的历史为背景&#xff0c;讲述了魏…

Mongodb使用$pop删除数组中的元素

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第67篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

使用PyTorch实现LSTM生成ai诗

最近学习torch的一个小demo。 什么是LSTM&#xff1f; 长短时记忆网络&#xff08;Long Short-Term Memory&#xff0c;LSTM&#xff09;是一种循环神经网络&#xff08;RNN&#xff09;的变体&#xff0c;旨在解决传统RNN在处理长序列时的梯度消失和梯度爆炸问题。LSTM引入了…

vue格网图

先看效果 再看代码 <n-gridv-elsex-gap"20":y-gap"20"cols"2 s:2 m:3 l:3 xl:3 2xl:4"responsive"screen" ><n-grid-itemv-for"(item,index) in newSongList":key"item.id"class"cursor-pointer …

C# OpenCvSharp Mat操作-创建Mat-ones

ones 函数用于创建一个全为“1”的矩阵&#xff08;Mat&#xff09;&#xff0c;可以用于各种图像处理和计算机视觉任务。下面我将详细解释每个重载版本的 ones 函数&#xff0c;并提供相应的示例代码。&#x1f4f8; 1️⃣ ones(int rows, int cols, int type) 这个重载函数…

VS - regsvr32.exe的官方工程

文章目录 VS - regsvr32.exe的官方工程概述笔记官方原版实现自己封装一个函数来干活(注册/反注册 COM DLL)END VS - regsvr32.exe的官方工程 概述 如果是要使用COM DLL&#xff0c; 必须先注册。 一般手工注册就要调用regsvr32.exe xx.dll 但是控制的不够细&#xff0c;且一般…

Spring学习笔记(九)简单的SSM框架整合

实验目的 掌握SSM框架整合。 实验环境 硬件&#xff1a;PC机 操作系统&#xff1a;Windows 开发工具&#xff1a;idea 实验内容 整合SSM框架。 实验步骤 搭建SSM环境&#xff1a;构建web项目&#xff0c;导入需要的jar包&#xff0c;通过单元测试测试各层框架搭建的正确…