C++多线程:互斥变量 std::mutex

描述

  • 头文件 <mutex>
  • 使用 std::mutex <variable>
  • 简介
    mutex是一种多线程变成中的同步原语,它能够让共享数据不被多个线程同时访问,它不支持递归得对互斥对象上锁
  • 特点
    • 用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex
    • 线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock )
    • 调用方线程在调用 lock 或 try_lock 前必须不占有 mutex ,否则无法获取<mutex>变量

成员函数

  • 构造函数
    std::mutex::mutex

    constexpr mutex() noexcept;(1)
    mutex( const mutex& ) = delete;(2)
    

    (1)构造mutex实例,mutex实例构造完成之后是处于unlocked状态
    (2)mutex的拷贝构造函数是不存在的

  • std::mutex::~mutex析构函数
    销毁互斥变量
    若互斥为任何线程占有,或若任何线程在保有任何互斥的所有权时终止,则行为未定义

  • 赋值运算符,不存在

  • std::mutex::lock
    锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁
    错误发生时抛出 std::system_error ,包括底层操作系统阻止 lock 满足其规定的错误。在抛出任何异常的情况下,不锁定互斥。所以,直接使用mutex的lock成员是无处法处理异常的情况。

    #include <iostream>
    #include <chrono>
    #include <thread>
    #include <mutex>
     
    int g_num = 0;  // protected by g_num_mutex 共享数据
    std::mutex g_num_mutex;
     
    void slow_increment(int id) 
    {
        for (int i = 0; i < 3; ++i) {
            g_num_mutex.lock();
            ++g_num;
            std::cout << id << " => " << g_num << '\n';
            g_num_mutex.unlock();
            	
     		//chrono是C++的时间库,提供1s线程的休眠
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
     
    int main()
    {
    	//0号线程和1号线程交叉执行对全局变量g_num对累加操作,执行完成之后sleep 1秒
        std::thread t1(slow_increment, 0);
        std::thread t2(slow_increment, 1);
        t1.join();
        t2.join();
    }
    

    输出如下

    0 => 1
    1 => 2
    0 => 3
    1 => 4
    0 => 5
    1 => 6
    
  • std::mutex::try_lock
    尝试锁定互斥。立即返回。成功获得锁时返回 true ,否则返回 false。
    若此操作返回 true ,则同一互斥上的先前 unlock() 操作同步于(定义于 std::memory_order )它。注意若此操作返回 false ,则先前的 lock() 不与之同步

    #include <chrono>
    #include <mutex>
    #include <thread>
    #include <iostream> // std::cout
     
    std::chrono::milliseconds interval(100);
     
    std::mutex mutex;
    int job_shared = 0; // 两个线程都能修改 'job_shared',
        // mutex 将保护此变量
     
    int job_exclusive = 0; // 只有一个线程能修改 'job_exclusive'
        // 不需要保护
     
    // 此线程能修改 'job_shared' 和 'job_exclusive'
    void job_1() 
    {
        std::this_thread::sleep_for(interval); // 令 'job_2' 持锁
     
        while (true) {
            // 尝试锁定 mutex 以修改 'job_shared'
            if (mutex.try_lock()) {
                std::cout << "job shared (" << job_shared << ")\n";
                mutex.unlock();
                return;
            } else {
                // 不能获取锁以修改 'job_shared'
                // 但有其他工作可做
                ++job_exclusive;
                std::cout << "job exclusive (" << job_exclusive << ")\n";
                std::this_thread::sleep_for(interval);
            }
        }
    }
     
    // 此线程只能修改 'job_shared'
    void job_2() 
    {
        mutex.lock();
        std::this_thread::sleep_for(5 * interval);
        ++job_shared;
        mutex.unlock();
    }
     
    int main() 
    {
    	/*
    	可以看到job1中的实现,使用的是try_lock获取锁,因为刚开始job1线程
    	会进行100毫秒的休眠,所以cpu会先去执行job2,但是job2使用的是lock成员
    	所以在job2中sleep 500毫秒的过程中执行job1时try_lock返回false,则执行exclusive输出.
    	当job2获取不到锁,job1休眠结束之后释放锁,则job1重新获取锁成功
    	*/
        std::thread thread_1(job_1);
        std::thread thread_2(job_2);
     
        thread_1.join();
        thread_2.join();
    }
    

    输出如下

    job exclusive (1)
    job exclusive (2)
    job exclusive (3)
    job exclusive (4)
    job shared (1)
    
  • std::mutex::unlock解锁mutex实例
    使用前提是当前线程必须被mutex的实例锁定,否则改函数的调用是未定义的
    注意,当前成员与lock()成员,一般不直接调用,因为对异常情况的处理并不友好(程序锁定期间发生异常,进程就直接退出),所以一般与std::unique_lock 与 std::lock_guard 管理排他性锁 一起使用
    具体使用实例可以参考如上两个代码。

总结

mutex互斥变量,并提供来了独占锁特性。为C++提供了多线程访问共享数据的保护措施,同时引入了像unique_locklock_gurad异常处理锁机制来规避mutex的成员处理异常情况的不足。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值