首页 > 其他分享 >【知识点】std::thread::detach std::lock_guard std::unique_lock

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

时间:2024-06-13 15:57:41浏览次数:11  
标签:std 知识点 thread lock 互斥 锁定 id

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

标签:std,知识点,thread,lock,互斥,锁定,id
From: https://blog.csdn.net/qq_33673253/article/details/139619796

相关文章

  • Linux时间子系统1:gettimeofday和clock_gettime实现分析
    1.Linux用户态获取时间的函数a.秒级别的时间函数:time和stimetime和stime函数的定义如下:#include<time.h>time_ttime(time_t*t);intstime(time_t*t);        time函数返回了当前时间点到linuxepoch的秒数(内核中timekeeper模块保存了这个值,timekeeper->x......
  • 计算机组成原理历年考研真题对应知识点(数制与编码)
    目录2.1数制与编码2.1.1进位计数制及其相互转换【命题追踪——采用二进制编码的原因(2018)】【命题追踪——十进制小数转换为二进制小数(2021、2022)】2.1.2定点数的编码表示【命题追踪——补码的表示范围(2010、2013、2014、2022)】【命题追踪——补码和真值的相互转......
  • 10.C语言for循环和跳出循环的知识点
    C语言for循环、continue和break知识点3.13for循环3.14for的一些用法3.15continue和break的作用3.16嵌套的规律3.17—作业3.13for循环概述和while的对比#include<stdio.h>intmain(){ intdata; //for(条件附初值;判断临界点;条件改变)//判断、执行循......
  • 【Blockly开发教程】06 Workspace概念
    文章目录你将收获WorkspaceWorkspace是什么Workspace组成Workspace作用创建WorkspaceWorkspace对象属性和方法保存、加载工作区保存(序列化)加载(反序列化)其他方法文章链接你将收获如何配置和使用workspace。WorkspaceWorkspace是什么在Blockly......
  • 【Test 66 】 高阶数据结构 二叉搜索树 必会知识点!
    文章目录1.二叉搜索树的概念2.二叉搜索树K模型的代码实现2.1Find()查找的实现2.2Insert()插入的实现2.3InOrder()中序遍历的实现2.4Erase()删除的实现3.二叉搜索树的KV模型4.二叉搜索树的性能分析1.二叉搜索树的概念......
  • 【java问答小知识8】一些Java基础的知识,用于想学习Java的小伙伴们建立一些简单的认知
    Java中的"java.util.IdentityHashMap"如何比较键?回答:"java.util.IdentityHashMap"使用==操作符来比较键,即它比较的是引用身份。Java中的"java.util.EventListener"接口有什么作用?回答:"java.util.EventListener"接口是所有事件监听器接口的基接口,用于定义事件处理方法......
  • Maven无法下载依赖之 maven-default-http-blocker (http://0.0.0.0/): Blocked mirror
    问题:maven-default-http-blocker(http://0.0.0.0/):Blockedmirrorforrepositories:原因:名称为maven-default-http-blocker的拦截器拦截了库的镜像。场景:在企业内部架设了一个Nexus的私有库,库的地址是:http://hostname:8081/repository/maven-public/,于是在项目中需要从这......
  • Unix socket lock file is empty /var/run/mysqld/mysqld.sock.lock
    介绍当使用MySQL数据库时,有时候会遇到一个错误信息,提示“Unixsocketlockfileisempty:/var/run/mysqld/mysqld.sock.lock.”这个错误一般是由于MySQL服务器无法启动造成的。在本文中,我们将探讨这个错误的原因和解决方案。错误原因/var/run/mysqld/mysqld.sock.lock......
  • windows server 2019 操作步骤和知识点(第一节)
    windowsserver1.1vmwareworkstation作用模拟硬件模拟操作系统步骤安装1模拟硬件文件新建虚拟机典型稍后安装操作系统Mcirosoftwindowswindows10X64win10-1d:/xujiji/win10-12模拟操作系统CD\DVD(SATA)使用ISO映像文件d:\iso\win10..........
  • Interlocked 为多个线程共享的变量提供原子操作 多线程重入
    Interlocked可以为多个线程共享的变量提供原子操作主要使用的读写方法varrunningState=Interlocked.Read(refisRunning);Interlocked.Exchange(refisRunning,0);可以配合lock实现业务常用方法Add(Int32,Int32) 对两个32位整数进行求和并用和替换第一个整数,上述操......