个别时候,我们要读取非常大的文件,如 5GB 要把文件读出,再写进,耗费的是CPU利用率与内存以及IO的频繁操作。这显然是
令用户难以忍受的
为了解决这个吃内存,占CPU,以及IO瓶颈,windows核心编程提供了内存映射文件技术;
因为数据非常大,还要处理,我采用了,内存映射+线程池;
先看一下内存映射读数据:
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
string GetValue(const TCHAR *, const TCHAR *); //根据name得value
void main(int argc, char* argv[])
{
// 创建文件对象(C: est.tsr)
HANDLE hFile = CreateFile("C:/test.tsr", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("创建文件对象失败,错误代码:%d ", GetLastError());
return;
}
// 创建文件映射对象
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hFileMap == NULL)
{
printf("创建文件映射对象失败,错误代码:%d ", GetLastError());
return;
}
// 得到系统分配粒度
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD dwGran = SysInfo.dwAllocationGranularity;
// 得到文件尺寸
DWORD dwFileSizeHigh;
__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
// 关闭文件对象
CloseHandle(hFile);
// 偏移地址
__int64 qwFileOffset = 0;
// 块大小
DWORD dwBlockBytes = 1000 * dwGran;
if (qwFileSize < 1000 * dwGran)
dwBlockBytes = (DWORD)qwFileSize;
if (qwFileOffset >= 0)
{
// 映射视图
TCHAR *lpbMapAddress = (TCHAR *)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,
0, 0,
dwBlockBytes);
if (lpbMapAddress == NULL)
{
printf("映射文件映射失败,错误代码:%d ", GetLastError());
return;
}
//-----------------------访问数据开始-------------------------
cout<<GetValue(lpbMapAddress,"SEU07201213")<<endl;
getchar();
//-----------------------访问数据结束-------------------------
// 撤销文件映像
UnmapViewOfFile(lpbMapAddress);
}
// 关闭文件映射对象句柄
CloseHandle(hFileMap);
}
string GetValue(const TCHAR *lpbMapAddress, const TCHAR *sName)
{
string sValue; // 存放 = 后面的value值
TCHAR *p1 = NULL, *p2 = NULL; // 字符指针
if((p1 = strstr(lpbMapAddress,sName)) != NULL) // 查找sName出现位置
{
if(p2 = strstr(p1,"/r/n")) *p2 = '/0'; // 查找"/r/n"(换行)出现位置
sValue = p1+strlen(sName)+strlen("="); // 指针移动"sName"+"="之后
*p2 = '/r'; // 还原*p2值,因为不还原会改变原文件结构
}
return sValue;
}
以上实现了根据索引name匹配value的简单过程,经测试,同样25W行文件,匹配耗费1秒不到,且
不占本进程内存。
以上因为没有直接使用读入内存再处理的方式,大大节约了内存;再就是采用这种方式,减少了IO,时间也大大减少;
但 5GB的数据,如果这样读加处理,还是要4分钟左右;
所以我加上了线程池,这样,最后的效果是 40秒左右,勉强可以接受;
void GetFloatDataByPool(vtkPoints * points, char * fileBuffer, int count, int jump)
{
char midData[50] = { 0 };
int index = 0;
double arr[3];
for (int j = 0; j < count * jump; j++)
{
fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
fileBuffer++;
}
for (int i = 0; i < jump; i++)
{
for (int j =0; j < 3; j++)
{
arr[j] = atof(fileBuffer);
fileBuffer = (char*)memchr((void*)fileBuffer, ' ', 10);
fileBuffer++;
}
fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
fileBuffer++;
points->InsertPoint(index, arr[0], arr[1], arr[2]);
index++;
}
}
ReadPtsByThreadPool()
{
ThreadPool * executor = ThreadPool::GetInstance();
//------------------------------------------------------内存映射方法;
//创建或打开文件内核对象;
HANDLE fileH = CreateFile("D:\\workspace\\sj\\aa.pts",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fileH == INVALID_HANDLE_VALUE)
{
cout << "error in CreateFile" << endl;
//return -1;
}
//创建一个文件映射内核对象;
HANDLE mapFileH = CreateFileMapping(fileH,
NULL,
PAGE_READWRITE,
0,
0,
"Resource ");
if (mapFileH == NULL)
{
cout << "error in CreateFileMapping" << endl;
//return -1;
}
//将文件数据映射到进程的地址空间;
char * mapH = (char *)MapViewOfFile(mapFileH,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (mapH == NULL)
{
cout << "error in MapViewOfFile" << endl;
//return -1;
}
//读取数据;
char *fileBuffer = mapH;
int times = atoi(fileBuffer);//300000 * 34;
fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
fileBuffer++;
int dividCount = times / 4;
std::future<void> ft[4];
for (int s = 0; s < 4; s++)
{
char *tp1 = fileBuffer;
ft[s] = executor->AddWork(GetFloatDataByPool, mSubPoints[s], tp1, s, dividCount);
}
for (int s = 3; s >=0; s--)
{
ft[s].get();
}
//for (int index = 0; index < dividCount * 4; index++)
//{
// points->InsertPoint(index, arr[0], arr[1], arr[2]);
//}
//points->InsertPoint(index, arr[0], arr[1], arr[2]);
//index++;
//关闭句柄;
UnmapViewOfFile(mapH);
CloseHandle(mapFileH);
CloseHandle(fileH);
}
完整代码样例: