关于程序的数据紧急备份的预案

文章介绍了在程序崩溃或电脑断电等异常情况下,如何通过实时保存到本地文件和使用内存映射文件来备份数据。提出了一个利用单例设计模式确保数据一致性并支持多线程安全访问的备份类实现,包括数据更新、自动更新、文件移除和读取等功能。
摘要由CSDN通过智能技术生成

问题:在程序突然崩溃时或者突然电脑掉电,此时程序中的数据将会丢失,那么如何将数据紧急备份呢?

解决方案:

1.保存到本地文件

在程序运行时,实时将数据备份到本地文件,这样,在程序重新启动或者恢复后,可以读取文件中的备份数据

2.使用内存映射文件

实际上,操作系统提供了一种使用内存映射文件的方法,可以将文件映射到内存中,从而实现数据的实时备份和持久化。

思路:参考一般软件的数据紧急备份处理,发现普遍使用以下机制:首先,在程序正常退出时,即使用软件的用户正常保存了数据并正常退出了软件,此时将会把数据备份的临时文件删除,因为数据已经正常保存,不需要临时文件了;如果程序异常退出,即奔溃、断电、或者没有保存数据的情况下退出的,再重新启动程序时,会给出一个是否恢复异常退出前的数据选项,确定后会把本地保存的临时数据文件读取出来。针对这一机制,可以得到如下的流程图,根据此图来实现备份类的功能。

注意:考虑到可能会有同时打开多个文件的情况,此时可能会多个对象操作同一个文件的读写,导致数据读写错误,这里我采用单例设计模式,不允许在类外调用构造和析构,保证对象的生存周期随主程序一起。在多个线程中获取同一个对象,操作同一个文件,在读写之前上锁,此类的自动更新数据接口有循环写入操作,应该在子线程中去调用这个接口,在回收线程之前,先设置条件变量。

上代码:

//backup.hpp
#ifndef _BACKUP_H_
#define _BACKUP_H_
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include <memory>
#include <mutex>
#include <iostream>
#include <filesystem>
#include <ctime>
#include <chrono>
#include <atomic>
using namespace std;
namespace fs = std::filesystem;
/自定义数据结构体,以下成员变量为示例,实际使用需按数据结构重新定义/
struct dataInfo {
	double x;
	double y;
	double z;
	string pointName;
	// 构造函数
	dataInfo(double x, double y, double z, string pointName) : x(x), y(y),z(z),pointName(pointName) {}
};
typedef vector<dataInfo> Data;

/备份单例类///
class backup {
public:
	/*
	@brief:数据更新接口
	@param:更新的数据的指针
	@return:
	*/
	void updataFile(const Data* dataPtr);
	/*
	@brief:数据自动更新接口
			注意,在调用这个接口的时候需要使用开辟一个线程去调用,不能在主线程中使用,会导致阻塞
			在回收开辟的线程的时候需要先调用StopAuto();再jion(),不然可能存在回收失败的问题
	@param:
			dataPtr:更新的数据的指针;
			filepath:文件路径
	@return:
	*/
	void autoUpdataFile(const Data* dataPtr, std::string filepath);
	/*
	@brief:临时文件移除接口
	@param:
	@return:
	*/
	void removeFile();
	/*
	@brief:设置自动更新开关接口
	@param:
	@return:
	*/
	void StopAuto();
	/*
	@brief:读取临时数据文件的数据接口
	@param:
	@return:返回数据的指针
	*/
	Data* readFile();
	/*
	@brief:获取单例对象的接口
	@param:
	@return:返回单例对象的指针
	*/
	static shared_ptr<backup> getBackupInstance();
private:
	string mfilename;
	Data   mfileData;
	size_t msize;
	std::mutex mutex;
	atomic<bool>mstopAuto=false;
	static shared_ptr<backup> instance;
private:
	/*
	@brief:私有构造函数,防止类外调用
	@param:
	@return:
	*/
	backup() {
		cout << "创建了一个备份类单例对象" << endl;
	}
	//backup(const backup&);
	//backup& operator=(const backup);

	/*
	@brief:私有析构函数,防止类外调用
	@param:
	@return:
	*/
	~backup() {
		mfileData.clear();
		cout << "销毁了一个备份类单例对象" << endl;
	}
	/*
	@brief:自定义的回收资源的函数,作为shared_ptr的自定义删除器
	@param:对象指针
	@return:
	*/
	static void DestroyInstance(backup* x);

};

#endif



















//backup.cpp
#include "backup.hpp"
void backup::updataFile(const Data* dataPtr) {
	ofstream ofs(mfilename, ios::binary | ios::out | ios::trunc);
	if (!ofs)
	{
		ofs.close();
		throw runtime_error("Failed to open file\n");
	}
	msize = dataPtr->size() * sizeof(int);
	int size = dataPtr->size();
	std::unique_lock<std::mutex> lock(mutex);
	//ofs.write((const char*)(dataPtr), msize);
	ofs.write((const char*)(&size), sizeof(int));//先写入容器大小,读的时候根据容器大小来读
	//以下部分根据容器中的元素类型来写
	for (auto& p : *dataPtr) {
		double x = p.x;
		double y = p.y;
		double z = p.z;
		string pointname = p.pointName;
		int namesize = pointname.size();//点的名称的长度,读的时候根据长度来读名称,也方便在读的时候开辟大小合适的空间
		ofs.write((const char*)(&x), sizeof(double));
		ofs.write((const char*)(&y), sizeof(double));
		ofs.write((const char*)(&z), sizeof(double));
		ofs.write((const char*)(&namesize), sizeof(int));
		ofs.write((const char*)(pointname.c_str()), namesize);
	}
	lock.unlock();
	ofs.close();
	
}
void backup::removeFile() {
	try {
		// 删除备份文件
		fs::remove(mfilename);
		cout << "remove success" << endl;
	}
	catch (fs::filesystem_error& ex) {
		std::cerr << "Failed to remove file: " << ex.what() << std::endl;
	}
}
Data* backup::readFile() {
	
	ifstream ifs(mfilename, ios::binary | ios::in);
	if (!ifs) {
		ifs.close();
		throw runtime_error("Failed to read file\n");
	}
	std::unique_lock<std::mutex> lock(mutex);
	//ifs.read((char*)(&mfileData), msize);
	int size;
	ifs.read((char*)(&size), sizeof(int));//先读容器大小
	//以下部分根据容器中的元素类型来写
	for (int i = 0; i < size; ++i) {
		double x, y, z;
		int namesize;
		ifs.read((char*)&x, sizeof(double));
		ifs.read((char*)&y, sizeof(double));
		ifs.read((char*)&z, sizeof(double));
		ifs.read((char*)&namesize, sizeof(int));
		char* namebuffer = new char[namesize + 1];
		ifs.read(namebuffer, namesize);
		namebuffer[namesize] = '\0';
		string name(namebuffer);
		delete[] namebuffer;
		mfileData.push_back(dataInfo(x,y,z,name));
	}

	lock.unlock();
	ifs.close();
	return &mfileData;
}
void backup::autoUpdataFile(const Data* dataPtr,std::string filepath) {
	mfilename = filepath;
	while (!mstopAuto)
	{
		updataFile(dataPtr);
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
}
void backup::StopAuto(){
	mstopAuto = true;
}


shared_ptr<backup>  backup::instance(new backup(), backup::DestroyInstance);
shared_ptr<backup>	backup::getBackupInstance() {
	return instance;
}
void backup::DestroyInstance(backup* x) {
	delete x;
	cout << "在自定义函数中释放实例" << endl;
}






































//ConsoleApplication1.cpp
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <thread>
#include <algorithm>
#include <numeric>
#include <atomic>
#include "backup.hpp"
// 测试并发性能
void testConcurrency(size_t threadNum, const std::string& filePath)
{
	std::cout << "Testing concurrency performance..." << std::endl;
	// 启动多线程并发更新数据
	std::vector<std::thread> threads;
	for (size_t i = 0; i < threadNum; ++i)
	{
		threads.emplace_back([&filePath, i]()
		{
			// 获取backup 对象
			auto backupInstance = backup::getBackupInstance();
			std::vector<dataInfo> newData = {
				{1.1+i,2.2 + i,3.3 + i,"point1"},
				{3.3 + i,6.6 + i,9.9 + i,"point2"}
			};
			//std::iota(newData.begin(), newData.end(), i);
			//std::cout << "thread"<<i<<"  newData data sum: " << std::endl << std::accumulate(newData.begin(), newData.end(), 0) << std::endl;
			backupInstance->autoUpdataFile(&newData, filePath);
		});
	}
#if 0
	// 启动备份数据监控线程
	std::thread monitor([&backupInstance]()
	{
		while (true)
		{

			std::this_thread::sleep_for(std::chrono::milliseconds(100));
		}
	});
#endif
	// 等待一段时间
	std::this_thread::sleep_for(std::chrono::seconds(1));

	// 停止并清理所有线程
	backup::getBackupInstance()->StopAuto();
	for (auto& thread : threads)
	{
		thread.join();
	}
	//monitor.detach();

	// 输出备份数据
	auto backupData = backup::getBackupInstance()->readFile();

	for (auto& t : *backupData) {
		std::cout << "pointName: " << t.pointName << std::endl;
		std::cout << "x: " << t.x << std::endl;
		std::cout << "y: " << t.y << std::endl;
		std::cout << "z: " << t.z << std::endl;
	}
	
	// 删除备份文件
	backup::getBackupInstance()->removeFile();
}
int main()
{

	// 进行并发性能测试
	testConcurrency(5, "mesh.data");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值