C++11中使用多线程操作(编译器VS2017)

在C11中采用了thread创建多线程,以前的AfxBeginThread()这种方式不进行使用。

首先,使用线程函数需要添加头文件#include <thread>

因为使用以下使用的是多线程方式,所以也需要使用互斥操作#include <mutex>

第一步:线程的创建,在类中实现创建

std::thread pThread = std::thread(ThreadDataOperation, this);

当前显示的是无参数的线程函数的调用,使用了线程创建之后,必须要明确 join() ? detach() 的方式,如果不写,会在当前位置显示异常。在这里我用到的方式是detach()

pThread.detach();

在此,使用detach()时,有一个特别严重的问题,就是:主线程与当前创建的线程分离,如果创建的线程中存在指针时,主程序结束,指针则变成了非法指针,引起崩溃。

此时,需要用加锁以及变量控制的方式来规避这个问题。

使用变量控制的方式,大家应该都不会陌生,举一个简单的例子

//以下是线程函数内部
{
    while(true)
    {
        if(bStop == true)
        {
            break;
        }
        /*
            线程中的具体操作流程
        */
    }
    bEnd = true; //走到了线程的最后一步
}

当满足指定条件时,退出线程,时线程自动结束

在线程中使用加锁的方式,方式detach()出现野指针现象

下面使用一种比较笨的方式来停止线程。

bStop = true;//停止当前正在运行的线程
while(true)
{
    if(bEnd == true)
    {
        TRACE("当前线程已经停止运行\n");//输出log
    }
    else
    {
        Sleep(100); //线程未停止
    }
}

在这里,我用了一个bEnd的变量来判断是否运行到了线程的最后一步,确保线程函数运行完成。

如果不想用这种比较笨的方式,下面我们可以用加锁的方式来控制detach()的线程是否正常结束

有人会问,为什么要在线程调用函数中加锁?当使用detach()方式时,想要外界控制停止线程的运行时,单靠一个变量是无法准确的表达主线程结束时,确保detach()的线程要先结束。

{
    dataMutex.lock();
    /*
        线程中数据处理主要逻辑
    */
    dataMutex.unlock();
}

当外界停止线程的时候的操作

bStop = true; //停止当前正在运行的线程
dataMutex.lock();
dataMutex.unlock();

怎么又要在停止的时候加锁呢?

因为在线程中有一个加锁机制,当又来一个锁机制时,此时需要等待上一个锁执行完成之后才会执行后一个锁机制,属于排队执行。可以实现while循环的等待功能。

下面是多线程类核心代码

.h文件

#pragma once

#include <string>
#include <map>
#include <thread> //C++11 开线程
#include <mutex> //C++11 互斥

//当前结构用来记录每一条线程数据内容
typedef struct OperationData
{
	bool bStartThread; //是否开始线程?
	bool bEndPush; //是否停止此次线程的操作
	std::thread *pThread; //对应线程指针
	std::mutex dataMutex; //互斥加锁机制
	/*
		当前结构只是存储表达多线程操作,不存在数据交互
		如果想要实现真实数据操作,只需在此自行添加即可。
	*/
	OperationData():bStartThread(false), pThread(nullptr), bEndPush(false) {}
}s_threadData;

class COperationThread
{
public:
	COperationThread();
	~COperationThread();
	/*
		真实数据操作接口
	*/

	/*************************************************
	Function: StartVideoStream
	Description: 开启线程
	Parma:
		nThreadIndex:需要开启的线程编号
	Return: void
	*************************************************/
	void StartThread(int nThreadIndex);

	/*************************************************
	Function: 	StopThread
	Description: 停止指定的线程
	Parma:
		nThreadIndex:需要停止的线程编号
	Return: void
	*************************************************/
	void StopThread(int nThreadIndex);

protected:
	//开启线程的函数
	UINT ThreadOperationData(s_threadData* stInfo);
private:
	std::map<int, s_threadData*> m_mapThreadData; //存储多线程数据
};

.cpp实现操作文件

#include "pch.h"
#include "TestDemo.h"

COperationThread::COperationThread()
{
}

COperationThread::~COperationThread()
{
	//此时,需要将map容器中的数据释放
	std::map<int, s_threadData*>::iterator itmap = m_mapThreadData.begin();
	for (itmap ; itmap!= m_mapThreadData.end() ; itmap++)
	{
		if (itmap->second->bEndPush == false)
		{
			//TODO:线程还未停止,需要等待停止
			itmap->second->bStartThread = false;
			itmap->second->dataMutex.lock();
			itmap->second->dataMutex.unlock();
			//线程结束之后,销毁new出来的地址
			delete itmap->second;
			itmap->second = nullptr;
		}
		else
		{
			//TODO:线程已经关闭,直接销毁即可
			delete itmap->second;
			itmap->second = nullptr;
		}
	}
	m_mapThreadData.clear(); //清空容器,保证不存在任何泄漏
}

void COperationThread::StartThread(int nThreadIndex)
{
	//TODO:判断存储多线程的容器中是否存在当前要打开的编号数据?
	std::map<int, s_threadData*>::iterator itFind = m_mapThreadData.find(nThreadIndex);
	if (itFind == m_mapThreadData.end())
	{
		//TODO:容器中没有存在次编号的数据,需要进行存储,且开启一个新线程
		s_threadData* stInfo = new s_threadData(); //必须!!!
		//数据赋值
		stInfo->bEndPush = false; //当前线程操作未结束
		stInfo->bStartThread = true; //需要开启新的线程
		std::thread pThread = std::thread(&COperationThread::ThreadOperationData , this , stInfo);
		stInfo->pThread = &pThread;
		pThread.detach();
		m_mapThreadData.insert(std::make_pair(nThreadIndex, stInfo));
	}
	else
	{
		//TODO:已经存在当前编号的数据了,需要停止上一次的线程操作
		//这一步是整个类的核心关键
		if (itFind->second->bEndPush == false)
		{
			//TODO:线程还未正常退出时,停止当前的线程
			itFind->second->bStartThread = false;//变量控制停止
			itFind->second->dataMutex.lock(); //加锁
			itFind->second->dataMutex.unlock(); //解锁
		}
		else
		{
			//TODO:线程已经停止,重新开启一个线程
			itFind->second->bEndPush = false;
			itFind->second->bStartThread = true;
			std::thread pThread = std::thread(&COperationThread::ThreadOperationData, this, itFind->second);
			itFind->second->pThread = &pThread;
			pThread.detach();
		}
	}
}

void COperationThread::StopThread(int nThreadIndex)
{
	//TODO:查询指定编号的线程,进行停止
	std::map<int, s_threadData*>::iterator itFind = m_mapThreadData.find(nThreadIndex);
	if (itFind == m_mapThreadData.end())
	{
		//TODO:未找到对应编号的线程,自行处理
	}
	else
	{
		if (itFind->second->bEndPush == true)
		{
			//TODO:当前线程已经停止,无需再次停止,自行处理
		}
		else
		{
			//TODO:停止指定线程
			itFind->second->bStartThread = false;
			itFind->second->dataMutex.lock();
			itFind->second->dataMutex.unlock();
		}
	}
}

UINT COperationThread::ThreadOperationData(s_threadData * stInfo)
{
	stInfo->dataMutex.lock();
	//简单例子说明
	while (true)
	{
		if (stInfo->bStartThread == false)
		{
			break;
		}
		/*
			具体的线程数据操作
		*/
	}
	stInfo->bEndPush = true; //线程运行到最后
	stInfo->dataMutex.unlock();
	return 0;
}

以上就是在类中实现多线程操作的例子。只是写了线程的应用,对于数据的处理,可以根据需求更外添加。

以上就是我对C11多线程的理解,如果有疑问可以留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章