C++11多线程(二) 互斥量(锁)——四种互斥量

文章目录

  • C++11多线程(二) 四种锁的应用
    • mutex(互斥量)
      • lock(),unlock()
        • 封装应用
    • timed_mutex(时间互斥锁)
      • timed_mutex常用方法
        • try_lock_for():
        • try_lock_until():
    • recursive_mutex(递归锁)
    • recursive_timed_mutex
    • 补充

C++11多线程(二) 四种锁的应用

mutex(互斥量)

mutex,也就是最简单的互斥锁。使用lock(),unlock()函数来进行加锁解锁操作。

还记得在C++11多线程(一)中我提到的cout的不安全性吗?

由于多个线程同时在使用cout,因此输出流中的字符顺序就不像我们所期望的那样,于是,我们引入了锁这个概念。

这里补充一个概念:线程安全

当多个线程访问某一个类(对象或方法)时,对象对应的公共数据区始终都能表现正确,那么这个类(对象或方法)就是线程安全的。

lock(),unlock()

#include    

mutex Lock;

Lock.lock();
Lock.unLock();

//lock(),unlock()是成对出现的,就像new和delete一样。注意不要连续lock和unlock。
//Lock首字母大写,因为lock是一个函数(这里用lock只是形象一点),实际编写过程中还是用其他变量名

//当然还有try_lock(),查看补充部分

代码如下:

#include
#include
#include//引入头文件
using namespace std;
mutex m_lock;
void f() {
     
	m_lock.lock();
	cout << "正在执行" << this_thread::get_id()<<"A"<<"B"<<"C" << endl;
    //"A"<<"B"<<"C" 用于多次输出表示线程安全
	m_lock.unlock();
}

int main() {
     
	thread t1(f);
	thread t2(f);

	t1.join();
	t2.join();
	return 0;
}

执行结果:

C++11多线程(二) 互斥量(锁)——四种互斥量_第1张图片

  • 在t1,t2的运行过程中,当运行到第9行时,m_lock便会被其中一个线程锁定,而另一个线程就会尝试获得这把锁,

    若没有获得,便会阻塞在原地直到获得锁。

  • 换通俗的语法,你也可以理解为,lock代表尝试获得锁,成功获得继续执行,失败则阻塞直到获得锁。而unlock则代表释放锁的所有权。

    这样,我们就用m_lock来保证了这两个线程的输出完整性。

    但实际情况中,将一个mutex设置为全局变量不免有些浪费和不安全,因此我们最好使用一个类来处理锁和线程。

    封装应用

    下面这段代码,我们尝试封装一个队列。利用Push()和Pop()函数来进行操作。

    #include
    #include
    #include
    #include
    #include //rand()产生随机值
    using namespace std;
    class Test
    {
           
    public:
    	Test();
    	void Push(int i);
    	void Pop();
    private:
    	queue<int> m_que;
    	mutex m_lock;
    };
    
    Test::Test() {
           
    	m_que = queue<int>();
    }
    void Test::Push(int i = 1)
    {
           
    	m_lock.lock();     //写方法上锁
    	m_que.push(i);
    	cout << i << "被Push了" << endl;
    	m_lock.unlock();   //写方法解锁
    }
    void Test::Pop()
    {
           	
    	m_lock.lock();    //写方法上锁
    	int i;
    	if (!m_que.empty()) {
             //这里是必要的 queue不为空的话pop会报异常
    		i = m_que.back();
    		m_que.pop();
    		cout << i << "被Pop了" << endl;
    		m_lock.unlock();//写方法解锁
    		return;
    	}
    	cout << "无数据" << endl;
    	m_lock.unlock(); //写方法解锁
        //这里要注意考虑无数据情况下的解锁问题
    }
    int main() {
           
    	Test T_Class;
    	while (1) {
           
    		thread t1(&Test::Push, &T_Class, rand() % 10);//读线程
    		thread t2(&Test::Push, &T_Class, rand() % 10);
            //可以思考一下T_Class为什么用&以及第三个参数的意义
            //不明白的可以查看C++多线程(一)
    		thread t3(&Test::Push, &T_Class, rand() % 10);
    		thread t4(&Test::Pop, &T_Class);              //写线程
    		t1.join();
    		t2.join();
    		t3.join();
    		t4.join();
    	}
    	return 0;
    }
    //可以自己改变一下读线程和写线程的数量观察运行状态。
    

    像这样用来管理读写功能的锁可以使用 C++14的 shared_mutex,也就是读写锁。读写锁的效率会更高,我们只探讨了C++11,有兴趣的可以查阅相关资料

timed_mutex(时间互斥锁)

timed_mutex 和 mutex相似,也可以使用lock() / unlock()来获得 / 释放锁。

timed_mutex常用方法

  1. try_lock_for():

    传入一个时间作为参数,等待一段时间,这段时间内如果取得了锁,返回true,没有拿到返回false;

    我使用简洁的表达方式,具体参数意义可参考官方手册

    timed_mutex T_lock;
    
    if(T_lock.try_lock_for(std::chrono::milliseconds(100))){
           //如果100ms内获得了锁就返回true
        //chrono::milliseconds chrono是C++11下的一个时间库。感兴趣自行了解
        dosomething();//执行动作
        T_lock.unlock();//记得解锁,try_lock成功会上锁,补充有简单讲解。
    }else{
           
        dotherthing();
    }
    
  2. try_lock_until():

    传入一个未来的时间点,在时间点前获得了锁就返回true,否则返回false;

    timed_mutex T_lock;
    
    if (T_lock.try_lock_until(chrono::steady_clock::now() + std::chrono::milliseconds timeout(100))) {
           
    	dosomething();//执行动作
    	T_lock.unlock();//记得解锁
    }else {
           
    	dotherthing();
    }
    
    

剩下两个锁因为我用的不多,就不多做阐述,给出

recursive_mutex(递归锁)

递归的独占互斥量,允许同一个线程,同一个互斥量,多次被lock,用法和非递归的一样 跟windows的临界区是一样的,但是调用次数是有上限的,效率也低一些

https://blog.csdn.net/qq_40666620/article/details/102702176

recursive_timed_mutex

带超时的,递归的,独占互斥量,允许同一个线程,同一个互斥量,多次被lock,用法和非递归的一样

https://blog.csdn.net/qq_40666620/article/details/102702176

补充

bool mutex::try_lock()

bool recursive_mutex::try_lock()

bool timed_mutex::try_lock()

三个成员方法尝试获得锁,成功返回true,失败返回false

​ 注意:如果try_lock()返回了true,那调用此方法的mutex变量已经被上锁了,需要我们进行unlock。

感谢您的观看,要是出现没有讲解清楚或者错误的地方,欢迎您随时指出

你可能感兴趣的:(c++thread,c++,多线程)