问题:在程序突然崩溃时或者突然电脑掉电,此时程序中的数据将会丢失,那么如何将数据紧急备份呢?
解决方案:
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");
}