3.线程间共享数据

线程间数据共享存在的问题

多个线程同时读取一个变量没问题,多个线程同时写一个变量问题很大

利用互斥量去解决

mutex m;
m.lock();
Write(varible);
m.unlock();

由于每次都要unlock,类似于new和delete, 所以都采用RAII手法,这里用的是lock_guard(),使用方法如下:

{
	std::mutex m;
	lock_guard<std::mutex> guard(m);//离开作用域会自动解锁
	Write(varible);
}
互斥量存在的条件竞争问题

比如一个stack对象obj,当线程1在调用obj.size()的时候,线程2在执行obj.pop()函数,虽然每个函数都加锁,比如线程1在方向size=1,接下来做pop操作时候,线程2已经把数据pop出去了,这样一来就会报错,所以在设计多线程时候还是要想清楚。

互斥量存在的死锁问题
//线程1
m1.lock();
m2.lock();
Play(keyboard, mouse);
m1.unlock();
m2.unlock();
//线程2
m2.lock();
m1.lock();
Play(mouse,keyboard);
m2.unlock();
m1.unlock();

可以看到,当我们在用电脑时候,要鼠标和键盘一起使用,这里有个问题,就是线程1是先抢键盘,而线程2是先抢鼠标,这样两个线程就会出现死锁现象。解决方法是线程1和2都是先抢键盘再抢鼠标,或者同时锁住(C++提供了std::lock()函数)。

//方法一
//线程1,2
m1.lock();
m2.lock();
Play(keyboard, mouse);
m1.unlock();
m2.unlock();
//方法二
std::lock(m1, m2);
lock_guard<mutex> lock_a(m1, std::adopt_lock);//adopt_lock表示互斥已经锁了,不用在这里锁了
lock_guard<mutex> lock_a(m2, std::adopt_lock);

利用其他工具去解决

call_once,once_flag

多线程遇到的初始化问题,这个具体参考单例模式中遇到的问题,书本中提供了一段代码解释为什么double-check有问题,但是我觉得书上解释的有问题,本质double-check的逻辑有问题还是因为reorder的问题,和单例模式中的问题一样

 void do()
 {
        if(nullptr == m_log) //避免初始化后每次依然加锁,影响性能
        {
            Lock lock;
            if(nullptr == m_log)// 避免第一次初始化时候出现多个线程都进入了代码块,生成多个实列
            { m_log = new Log();}
        }
        m_log->do_something();
 }

可以知道其实当线程1已经new一个log对象的时候,即使还没完成构造就返回了指针,当线程2在判断m_log是不是为空时,发现不为空就直接do_something(), 但是此时线程1还没完成构造,这里为什么会出现这种情况是因为部分编译器优化导致的。如何解决这个问题?c++提供了call_once,once_flag的方式,简单的意思就是把初始化代码包裹成一个原子操作,具体实现是利用栅栏技术,反正就是保证初始化的完整,如果多个线程同时走到call_once的时候,会有一个线程被激活去处理init_func, 一旦处理完后,以后其他线程再遇到就不用看这一行代码了。

std::once_flag flag;
void do()
{
	  std::call_once(flag, init_func);
      m_log->do_something();
}
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值