c++线程同步案例分析

本文通过实例阐述了在开发实时仿真软件时,如何使用线程同步机制,尤其是互斥锁,以确保业务处理流程的线程安全,避免因数据竞争导致的内存冲突。作者提供了一个CThread和CProcess类的示例,展示了在输入接口和业务线程中正确使用锁的重要性。
摘要由CSDN通过智能技术生成

开发实时仿真软件时,一般留有实时输入数据接口,根据仿真步长接收实时数据。由于仿真软件还要进行业务处理,如果把业务处理流程直接放在实时输入接口内部,那么相当于把业务挂在了调用方的线程上,很容易把唯一的线程卡住,谈何实时仿真?所以,这里应该给业务运转单独起一个线程,输入接口只负责接收数据,业务线程专门负责内部计算,两个线程共同操作同一缓存,即线程同步。本文通过一个案例来分析线程同步的应用以及证明加锁的必要性。

首先,自定义一个线程基类。该类主要实现线程的开始、停止,以及用一个互斥锁保障线程安全。代码如下:

CThread.h 

#pragma once
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std;

class CThread
{
public:
	CThread();
	virtual ~CThread();

	void start();
	void stop();

protected:
	virtual void run() = 0;
	bool m_running;
private:
	thread m_thread;
	mutex m_mutex;
};

注意,这里把run函数写成了纯虚函数,即必须在子类里面实现。

CThread.cpp

#include "CThread.h"

CThread::CThread()
{
	m_running = false;
	cout << "进入基类线程!进行构造 CThread\n";
}

CThread::~CThread()
{
	cout << "进入基类线程! 进行析构~CThread!\n";
	if (m_running)
	{
		stop();
	}
}

void CThread::start()
{
	lock_guard<mutex> lock(m_mutex);
	if (!m_running)
	{
		m_thread = thread(&CThread::run, this);
		m_running = true;
	}
}

void CThread::stop()
{
	lock_guard<mutex> lock(m_mutex);
	if (m_running)
	{
		m_running = false;
		m_thread.join();
	}
}

接着,写一个业务类,即真正负责逻辑调度,各种业务相关计算的类。代码如下:

CProcess.h

#pragma once
#include "CThread.h"
#include <vector>

struct Struct_RTData
{
	double x;
	double y;
	double z;
	string id;
};

class CProcess : public CThread
{
public:
	CProcess();
	~CProcess();
	void DataRTInput(vector<Struct_RTData> vecRtData); //实时输入接口);

protected:
	void run();

private:
	vector<Struct_RTData> mVecRtData; //实时数据缓存,共享变量,累加和删除同步进行
	mutex mMutex; //锁
};

 CProcess.cpp

#include "CProcess.h"
#include <Windows.h>

CProcess::CProcess()
{
	start(); //构造时线程即开始
}

CProcess::~CProcess()
{

}

void CProcess::DataRTInput(vector<Struct_RTData> vecRtData)
{
	lock_guard<mutex> lock(mMutex);
	for (int i = 0; i < vecRtData.size(); i++)
	{
		mVecRtData.push_back(vecRtData[i]);
	}
}

void CProcess::run()
{
	while (m_running)
	{
		if (mVecRtData.size() > 0)
		{
			{
				lock_guard<mutex> lock(mMutex);
				mVecRtData.erase(mVecRtData.begin()); //删除共享数组首位的实时数据,表示这个数据已经计算完成
			}
			int time_ms = rand() % 1000; //耗时从0到1000毫秒随机变化
			double t = GetTickCount64(); //系统相对时间
			cout << "一帧数据业务处理完毕:" << t << "; size=" << mVecRtData.size() << " 耗时:" << time_ms << "ms\n";
			this_thread::sleep_for(std::chrono::milliseconds(time_ms));
		}
	}
}

这里要特别注意的是输入接口和线程函数里面都通过lock_guard<mutex>进行加锁,以解决两个线程竞争操作同一内存的冲突!

最后,给出main函数:

#include <iostream>
#include "CProcess.h"
#include <thread>
#include <chrono>
#include <string>

using namespace std;

int main()
{
	std::cout << "线程同步案例!\n";
	CProcess* p = new CProcess();
	this_thread::sleep_for(chrono::seconds(3));

	for (size_t i = 0; i < 100000; i++)
	{
		vector<Struct_RTData> tVec;
		for (size_t j = 0; j < 100; j++)
		{
			Struct_RTData tData;
			int tmp = i * j;
			tData.id = to_string(tmp);
			tVec.push_back(tData);
		}
		p->DataRTInput(tVec); //模拟发送了一帧数据
		this_thread::sleep_for(chrono::milliseconds(617)); //故意把仿真间隔设置成奇数
	}
	delete p;
}

为了证明这两处加锁的必要性,我们任意注释其中一处或者两处,运行一下代码,结果如下:

显然,这是访问内存冲突了。 

然后,我们恢复两处加锁,再看结果:

两个线程相安无事!

总结:对于两个线程竞争同一内存的场景,在操作内存处进行加锁保护可以避免内存冲突。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值