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

【知识点】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 的使用,可以更好地管理多线程环境下的资源同步,编写更安全和高效的代码。

相关文章:

  • 【CT】LeetCode手撕—121. 买卖股票的最佳时机
  • 在不使用js在情况下只用css实现瀑布流效果
  • 速盾:cdn加速怎么计费?
  • 二刷算法训练营Day29 | 回溯算法(5/6)
  • SortTable.js + vxe-table 实现多条批量排序
  • 第 4 章:从 Spring Framework 到 Spring Boot
  • PyCharm设置不默认打开上次的项目
  • Android 调用系统相册、系统相机拍照
  • MyBatis进行模糊查询时SQL语句拼接引起的异常问题
  • kubeadm快速部署K8S
  • 长亭雷池部署
  • 【云岚到家】-day03-1-门户等缓存方案选择
  • Django DetailView视图
  • 如何将NextJs中的File docx保存到Prisma ORM
  • 奇思妙想-可以通过图片闻见味道的设计
  • “大数据应用场景”之隔壁老王(连载四)
  • 10个最佳ES6特性 ES7与ES8的特性
  • 2017届校招提前批面试回顾
  • angular2 简述
  • mysql外键的使用
  • PHP 小技巧
  • SwizzleMethod 黑魔法
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • Vue--数据传输
  • 微信小程序填坑清单
  • 为视图添加丝滑的水波纹
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #{} 和 ${}区别
  • #DBA杂记1
  • (js)循环条件满足时终止循环
  • (Oracle)SQL优化技巧(一):分页查询
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (算法)Game
  • (算法二)滑动窗口
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (转) 深度模型优化性能 调参
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • .NET 依赖注入和配置系统
  • .NET技术成长路线架构图
  • .Net中间语言BeforeFieldInit
  • .project文件
  • @AutoConfigurationPackage的使用
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚
  • @RequestParam详解
  • @软考考生,这份软考高分攻略你须知道