C++线程学习一(线程介绍)

1、线程学习说明

        本文主要记录了线程学习的笔记,主要以示例的形式讲解每个接口的使用以及优缺点。线程的理论知识还没有系统的整理,所以后面以具体代码为主,会在每个代码段介绍对应使用的函数。

        文章主要是自己学习线程后,对线程学习笔记的整理。

 

2、线程与线程锁

         在使用线程的过程中,如果存在数据的读写,不可避免的要使用到线程锁。使用线程锁的目的是为了保护共享内存,防止在一个线程写数据的时候另外一个数据在读这一个数据,这就会导致数据出现异常。最好的方式是将这一块的数据锁起来,在写这个数据的时候其他线程不能进入,等写操作完成;释放权限给读操作。

         常用的线程锁有互斥量(后文主要介绍互斥量)、临界锁、自旋锁、条件变量等;

互斥量的使用:(mutex)
   【1】lock()   unlock()  需要成对使用,加锁后一定要解锁
   【2】std::lock_guard类模板

示例主要介绍了线程与互斥量的使用, 

#include <iostream>
#include <thread>
#include <mutex>
#include <list>

using namespace std;

class dataShare 
{
public:
	// 存数据
  void inMsgRecvQueue()
  {		
	for (int i = 0; i < 1000; i++)
	{			
	  m_Mutex.lock();
	  m_RecvQueue.push_back(i);
	  m_Mutex.unlock();
       // m_Mutex.unlock();    // 代码中一定要避免出现多次unlock(),会报错
                
	  // doSomething
	  // ...		
	}	
  }

private:
  std::list<int> m_RecvQueue;
  //互斥量
  std::mutex m_Mutex;
};

int main()
{

  dataShare datas;
  std::thread inMsgObj(&dataShare::inMsgRecvQueue, &datas);
  inMsgObj.join();

  system("pause");
  return 0;
}

3、互斥锁注意事项

        【1】互斥量是一个类对象,多个线程尝试用lock()成员函数来加锁,只有一个线程能锁成功(成功标志是lock()函数返回)如果没锁成功,流程就会一直卡在lock()这里不断尝试
        【2】互斥量使用时需要谨慎,范围保护太大,影响代码运行质量;保护范围太少,达不到保护的作用;
        【3】为了防止忘记unlock(),引入std::lock_guard的类模板,类似于智能指针(unique_ptr<>);

为了较好的理解std::lock_guard的类模板的使用,下面给出lock_guard()的使用示例,只需要对上一段代码inMsgRecvQueue函数进行微调即可:

void inMsgRecvQueue()
{
	for (int i = 0; i < 1000; i++)
	{
	/*
	 1.使用lock_guard()后就不需要unlock释放线程
         2.在lock_guard()使用时添加作用域,防止线程作用范围太广
        */
		{
			std::lock_guard<std::mutex> lockGuard(m_Mutex);
			m_RecvQueue.push_back(i);
		}	
		// doSomething
		// ...	
	}
}

4、死锁

        两个线程A、B,两个锁(lockA,unlockA)、(lockB,unlockB),其中线程A使用(lockA,ublockB),线程B使用(lockB,ublockA),此时就成了死锁
        描述的比较简单,可以自己思考一下,代码中也可以试试,死锁解决比较简单,定位问题会比较难,所以代码中需要注意;

下面示例可能看着有点傻,但是在实际项目中,框架比较复杂的前提下还是会出现这类问题,在此主要还是介绍一下死锁;

class dataShare 
{
public:

    // 存数据
    void inMsgRecvQueue()
	{	
		for (int i = 0; i < 1000; i++)
		{		
		    m_Mutex.lockA();
                    m_Mutex.lockB();
		    m_RecvQueue.push_back(i);
                    mutexA.unlock();
			mutexB.unlock();

			// doSomething
			// ...		
		}	
	}

	// 取数据
	void outMsgRecvQueue()
	{
		for (int i = 0; i < 1000; i++)
		{
		    m_Mutex.lockB();
                    m_Mutex.lockA();
                    if(!m_RecvQueue.empty())
                    {
                        cout<< m_RecvQueue.front() << endl;		// 返回第一个元素,但是不检查元素是否存在;
		        m_RecvQueue.pop_front();			    // 移除第一个元素但不反回
                    }
		        mutexA.unlock();
                    mutexB.unlock();
		}
	}
	
private:
	std::list<int> m_RecvQueue;

	//互斥量
	std::mutex m_MutexA;
        std::mutex m_MutexB;

};

避免使用死锁可以使用std::lock_guard()

下面一个示例也可以避免死锁

	void inMsgRecvQueue()
	{
		
		for (int i = 0; i < 1000; i++)
		{
			cout << "input list i:" << i << endl;

			// 相当于每个互斥量都调用了lock(),避免出现死锁
			std::lock(mutexA, mutexB);

			// 使用lock_guard的类模板,可以避免unlock()
			std::lock_guard<std::mutex> guard1(mutexA, std::adopt_lock);
			std::lock_guard<std::mutex> guard1(mutexB, std::adopt_lock);
				
                        m_RecvQueue.push_back(i);

			mutexA.unlock();
			mutexB.unlock();

			// doSomething
			// ...
		
		}
		
	}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值