在内存中使用多线程查找数据
前言
将文件加载到内存中,根据线程数量分块计算,每个线程开始查找.任务分解为:- 先获取文件的行数
- 根据行数分配内存大小
- 将文件以行的方式加载到内存中
- 通过计算,给每个线程进行分块
- 将查找的字符串进行输出
1.获取文件的行数
//根据文件地址,获取文件的行数
int getFileLine(char *path)
{
FILE *pReader = fopen(path, "rb");
if (pReader == NULL)
{
return -1;
}
else
{
int lineCount = 0;
while (!feof(pReader))
{
char tmpStr[512] = { 0 };
fgets(tmpStr, 512, pReader);
lineCount++;
}
fclose(pReader);
return lineCount;
}
}
2.内存分配和加载文件到内存合为一个方法
//将文件加载到内存中
void loadFileInMemory(char *path)
{
if (path == NULL)
{
printf("文件地址不能为空!\n");
return;
}
p_Info = calloc(LINES, sizeof(char *)); //进行分配内存并初始化
FILE *pReader = fopen(path, "rb");
if (pReader == NULL)
{
printf("无法打开文件\n");
return;
}
for (int i = 0; i < LINES; i++) //知道行数
{
char tmpStr[512] = { 0 };
fgets(tmpStr, 512, pReader); //从文件流读取到字符串中
p_Info[i] = calloc(512, sizeof(char)); //根据字符串的长度进行分配内存
char *p = strcpy(p_Info[i], tmpStr); //将字符串拷贝到内存中
if (p == NULL)
{
printf("第%d行,文本内容 %s 出现问题!\n", i, tmpStr);
break;
}
}
fclose(pReader);
}
3.计算每个线程的起始地址,进行分块
//通过线程数量,计算每个线程执行的起始地址
void splitSegment(char *str)
{
int threadCount = 4; //这里先写死线程数量
hd = calloc(threadCount, sizeof(HANDLE)); //将线程句柄集合初始化内存
pQueryInfo = calloc(threadCount, sizeof(struct queryInfo)); //根据线程数量分配多个queryInfo
int lines = LINES;
int mod = lines%threadCount; //根据行数对线程数进行求余
if (mod == 0) //1.处理能整除的情况
{
for (int i = 0; i < threadCount; i++)
{
pQueryInfo[i].id = i;
pQueryInfo[i].findStr = str;
pQueryInfo[i].pStart = p_Info + i*(lines / threadCount);
pQueryInfo[i].len = lines / threadCount;
hd[i] = _beginthread(searchStr, 0, &pQueryInfo[i]);
}
}
else //2.处理不能整除的情况
{
//让线程数理减1整除,最后1个线程执行剩下的
int execCount = threadCount - 1;
for (int i = 0; i < execCount; i++)
{
pQueryInfo[i].id = i;
pQueryInfo[i].findStr = str;
pQueryInfo[i].pStart = p_Info + i*(lines / execCount);
pQueryInfo[i].len = lines / execCount;
hd[i] = _beginthread(searchStr, 0, &pQueryInfo[i]);
}
//最后线程,执行求余剩下的部分
{
int i = execCount;
pQueryInfo[i].id = i;
pQueryInfo[i].findStr = str;
pQueryInfo[i].pStart = p_Info + i*(lines / threadCount);
pQueryInfo[i].len = lines % execCount; //这里是求余剩下的数量
hd[i] = _beginthread(searchStr, 0, &pQueryInfo[i]);
}
}
}
4.查找字符串
//根据字符串进行查找
void searchStr(void *pInfo)
{
if (pInfo == NULL)
{
printf("查找数据不能为空!\n");
return;
}
struct queryInfo *pQueryInfo = pInfo;
for (int i = 0; i < pQueryInfo->len; i++)
{
if (pQueryInfo->pStart[i] != NULL)
{
char *pInfo = strstr(pQueryInfo->pStart[i], pQueryInfo->findStr);
if (pInfo != NULL)
{
printf("找到 %s\n", pQueryInfo->pStart[i]);
}
}
}
}
5.测试代码
#define LINES 1007579
struct queryInfo
{
int *pStart; //起始地址
char *findStr; //查找的字符串
int len; //文件的行数
int id; //线程id
};
struct queryInfo *pQueryInfo; //存放查询信息
HANDLE *hd; //存放线程句柄集合
char **p_Info; //存放文件每一行的地址
//释放文件加载到内存区域的这一部分内存
void freeMemory()
{
if (p_Info != NULL)
{
for (int i = 0; i < LINES; i++)
{
if (p_Info[i] != NULL)
{
free(p_Info[i]);
}
}
p_Info = NULL;
}
}
int main(int argc, char *argv[])
{
char filePath[64] = "G:\\bigdata\\kf\\1_1.txt";
//int lineCount = getFileLine(filePath); //只使用一次
//printf("lineCount=%d\n", lineCount);
loadFileInMemory(filePath);
while (1)
{
char tmpStr[64];
scanf("%s", tmpStr);
if (strcmp("exit", tmpStr) == 0) //输入exit的时候,释放内存,终止循环
{
freeMemory();
break;
}
splitSegment(tmpStr);
}
system("pause");
return 0;
}
结语
现在实现的方式,都是简单粗暴的,要是大文件,上几个g的文本,8g内存是不够用的,所以采用的都是讲大文件分割成小文件,在加载到内存上进行处理的.后面会建立索引和二分查找,进行文本的快速查找.
秋风
2016-07-17