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多線程的理解,如果有疑問可以留言。

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