使用多线程,将一组数据拆分成多组进行查询,提高查询效率!

并发的基本概念
所谓并发是在同一实体上的多个事件同时发生。并发编程是指在在同一台计算机上“同时”处理多个任务。

其实也就是多个工人共同工作,提高工作效率!

并行搜索算法,就是有一组特别大的数据,需要在这组数据里面统计某个数重复的次数。
按正常思维,从头开始遍历,直至最后;这样的话,数据量一大,效率就很低了,所花费的时间就很多了,因为是一个人在干活。
如果安排多个人一起干活,每个人负责某一个区域,那么效率就上来了!

所以,实现并行搜索算法需要使用到多线程的操作
如果不懂多线程的小伙伴,可以参考此篇博客去学习:


算法实现

当然,搜索操作是在线程方法里面去实现的,如下面就是线程函数,使用最简单的方式遍历:

// 线程处理的函数
unsigned int WINAPI ThreadProc(LPVOID lpParam) {
	search *s = (search *)lpParam;
	clock_t start, end;

	start = clock();	// 开始记录时间

	for (size_t i = s->start; i <= s->end; i++) {
		if (s->data[i] == NUMBER) {
			s->count++;
		}
	}
	
	end = clock();	// 结束记录时间

	printf("time = %d 毫秒\n", (int)(end - start) * 1000 / CLOCKS_PER_SEC);

	return 0;
}

创建多个线程去调用这函数,就可以实现多人工作的效果,提升效果!


代码:

数组个数是:(1024 * 1024 * 300) 等于 314572800。三亿多个数。

#include <iostream>
#include <stdio.h>
#include <Windows.h>
#include <time.h>
#include <process.h>

#define TEST_SIZE		(1024*1024*400)		// 数字的元素个数
#define NUMBER			888					// 查找的数字


typedef struct _search {
	int *data;		// 搜索的数据集
	size_t start;	// 搜索的开始位置
	size_t end;		// 索索地结束位置
	size_t count;	// 搜索的结果
}search;


// 线程处理的函数
unsigned int WINAPI ThreadProc(LPVOID lpParam) {
	search *s = (search *)lpParam;
	clock_t start, end;

	start = clock();	// 开始记录时间

	for (size_t i = s->start; i <= s->end; i++) {
		if (s->data[i] == NUMBER) {
			s->count++;
		}
	}
	
	end = clock();	// 结束记录时间

	printf("time = %d 毫秒\n", (int)(end - start) * 1000 / CLOCKS_PER_SEC);

	return 0;
}


int main(void) {
	int *data = NULL;
	int count = 0;	// 记录的数量
	int mid = 0;	// 数组中间索引

	search s1, s2;

	data = new int[TEST_SIZE];

	for (int i = 0; i < TEST_SIZE; i++) {
		data[i] = i;
	}

	mid = TEST_SIZE / 2;	// 计算中间值索引

	s1.data = data;
	s1.start = 0;	// 开始查找的索引位置
	s1.end = mid;	// 结束查找的索引位置
	s1.count = 0;	// 查找出的个数

	s2.data = data;
	s2.start = mid + 1;
	s2.end = TEST_SIZE - 1;
	s2.count = 0;


	UINT threadID1 = 0;	// 线程1的身份证
	HANDLE hThread1 = NULL;	// 线程1的句柄

	UINT threadID2 = 0;	// 线程2的身份证
	HANDLE hThread2 = NULL;	// 线程2的句柄

	// 创建线程1
	hThread1 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s1, 0, &threadID1);
	// 创建线程2
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s2, 0, &threadID2);

	// 合法性检查(检查线程是否创建成功)
	if (hThread1 == NULL || hThread2 == NULL) {
		printf("_beginthreadex() ERROR! \n");
		return -1;
	}


	// 等待线程执行完毕
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);

	printf("线程结束,结果是:count = %d\n", s1.count + s2.count);

	return 0;
}

运行截图:

并行查询不会用到索引_#include


一个线程用了304毫秒,另一个线程用了310毫秒!

当然结果肯定是1,因为数组中只有一个888.


为了凸显效果,我们将代码改一下,用4个线程进行搜索,看看每个线程所花费的时间是多少:

数组个数是:(1024 * 1024 * 300) 等于 314572800。三亿多个数。

#include <iostream>
#include <stdio.h>
#include <Windows.h>
#include <time.h>
#include <process.h>

#define TEST_SIZE		(1024*1024*300)		// 数字的元素个数
#define NUMBER			888					// 查找的数字


typedef struct _search {
	int *data;		// 搜索的数据集
	size_t start;	// 搜索的开始位置
	size_t end;		// 索索地结束位置
	size_t count;	// 搜索的结果
}search;


// 线程处理的函数
unsigned int WINAPI ThreadProc(LPVOID lpParam) {
	search *s = (search *)lpParam;
	clock_t start, end;

	start = clock();	// 开始记录时间

	for (size_t i = s->start; i <= s->end; i++) {
		if (s->data[i] == NUMBER) {
			s->count++;
		}
	}
	
	end = clock();	// 结束记录时间

	printf("time = %d 毫秒\n", (int)(end - start) * 1000 / CLOCKS_PER_SEC);

	return 0;
}


int main(void) {
	int *data = NULL;
	int count = 0;	// 记录的数量
	int mid = 0;	// 数组中间索引
	int mid1 = 0, mid2 = 0;	

	search s1, s2, s3, s4;

	data = new int[TEST_SIZE];

	for (int i = 0; i < TEST_SIZE; i++) {
		data[i] = i;
	}

	// 将数据分成四段
	mid = TEST_SIZE / 2;	// 中间值索引
	mid1 = mid / 2;			// 左边中间值索引	
	mid2 = (TEST_SIZE + mid) / 2;	// 右边中间值索引
	
	// 第一段
	s1.data = data;
	s1.start = 0;	// 开始查找的索引位置
	s1.end = mid1;	// 结束查找的索引位置
	s1.count = 0;	// 查找出的个数
	
	// 第二段
	s2.data = data;
	s2.start = mid1 + 1;
	s2.end = mid;
	s2.count = 0;

	// 第三段
	s3.data = data;
	s3.start = mid + 1;
	s3.end = mid2;
	s3.count = 0;

	// 第四段
	s4.data = data;
	s4.start = mid2 + 1;
	s4.end = TEST_SIZE - 1;
	s4.count = 0;


	UINT threadID1 = 0;		// 线程1的身份证
	HANDLE hThread1 = NULL;	// 线程1的句柄

	UINT threadID2 = 0;		// 线程2的身份证
	HANDLE hThread2 = NULL;	// 线程2的句柄

	UINT threadID3 = 0;		// 线程3的身份证
	HANDLE hThread3 = NULL;	// 线程3的句柄

	UINT threadID4 = 0;		// 线程4的身份证
	HANDLE hThread4 = NULL;	// 线程4的句柄

	// 创建线程1
	hThread1 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s1, 0, &threadID1);
	// 创建线程2
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s2, 0, &threadID2);
	// 创建线程3
	hThread3 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s3, 0, &threadID3);
	// 创建线程4
	hThread4 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &s4, 0, &threadID4);

	// 合法性检查(检查线程是否创建成功)
	if (hThread1 == NULL || hThread2 == NULL || hThread3 == NULL || hThread4 == NULL) {
		printf("_beginthreadex() ERROR! \n");
		return -1;
	}


	// 等待线程执行完毕
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);
	WaitForSingleObject(hThread3, INFINITE);
	WaitForSingleObject(hThread4, INFINITE);

	printf("线程结束,结果是:count = %d\n", s1.count + s2.count + s3.count + s4.count);

	return 0;
}

并行查询不会用到索引_c语言_02


看到没有,平均每个线程花费166毫秒,效率一下子又上去了!

当然结果肯定是1,因为数组中只有一个888.


总结

感觉并行搜索算法并不难,就是考你多线程的用法,把多线程搞定了,这个算法自然而然就会了!算是多线程的一个小例子吧!