你的位置:首页 > 操作系统

[操作系统]pthread Win32多线程编程的一些知识和感想


研究遗传算法的一大诟病就是每次运行程序的结果并不是完全一样的,有时候能找到最优解有时候找不到最优解,这就是遗传算法的概率性导致的。那么怎么评价你的方法的好坏呐,这时候就要多次独立运行程序最后取结果的平均值或者计算算法的运行成功率。那么问题就来了,遗传算法的运行时间本来就略长,尤其当测试数据集很大且数量很多的时候,做一次实验跑完所有数据的时间有时候有点让人难以接受。于是想到了使用多线程,这样就可以同时运行多组独立的实验,最后再整合所有子线程的输出数据,计算运行成功率或者取最优解的平均值即可,怀揣着这个想法,开始尝试着用多线程编程来加快实验进度。以下是这几天的学习、实验过程和结果。

本人一直用Windows下的VS 2010编程,看了很多关于多线程编程的博客和资料之后我决定不用win32的API来实现多线程(各种函数名太长看着都懒得搞),而使用pthread的windows开发包(下载地址http://sourceware.org/pthreads-win32/ 下载任意版本,本人下载的是最新版pthreads-w32-2-9-1-release.zip,在VS下的配置方法稍后描述)比较方便。So, 多线程编程即将搞起来~~~

第一步:在VS2010下配置pthread
下载好pthread之后解压到任意文件夹,解压之后包含三个文件夹 Pre-built.2 pthreads.2 以及QueueUserAPCEx。在配置中需要用到的只有第一个文件夹下的东西。打开Pre-built.2文件夹会开到三个文件夹分别是dll、include、lib以及一对其他文件,从这三个文件夹就可以看出接下来要怎么配置。

1.将include路径填加为VS的包含路径。 例如我的include路径是F:\C&C++\多线程编程\pthreads-w32-2-9-1-release\Pre-built.2\include。填加该路径至包含路径

打开 属性管理器->右击->属性->VC++目录->包含目录, 将要填加的目录复制粘贴进去点击确定(主意千万不要在路径前面填加“$”符号,否则出错)

 

2.填加库目录。同第一步,将lib文件夹的目录填加进库目录

3.填加lib文件到附加依赖项。属性管理器->右击->属性->链接器->输入->附加依赖项。将lib文件夹下的.lib文件文件名复制粘贴在该项目下,记得每一个lib文件名换行。

4.将dll文件夹下的dll文件复制粘贴到你的VS项目根目录下。就可以开始使用pthread在VS中写多线程程序了。

第二步.编程

下面是我的程序

#include"rcpsp.h"//自己编写的头文件#include<pthread.h>//pthread头文件,多线程的东西都在里面//===========10个线程=========================//每个线程是一次独立的实验,每组数据十次独立运行,所以编写十个子线程,以求快速完成实验void* process_0(void *arg);void* process_1(void *arg);void* process_2(void *arg);void* process_3(void *arg);void* process_4(void *arg);void* process_5(void *arg);void* process_6(void *arg);void* process_7(void *arg);void* process_8(void *arg);void* process_9(void *arg);//==========================================typedef struct{	//===========Input Variables============	....	//===========Output Variables===========	....}InputData;int main(void){	    pthread_t p0,p1,p2,p3,p4,p5,p6,p7,p8,p9;//定义pthread变量	InputData Data[10];    //子线程输入输出数据    //=================multi threads=========================			pthread_create(&p0,NULL,process_0,(void*)&Data[0]);		pthread_create(&p1,NULL,process_1,(void*)&Data[1]);		pthread_create(&p2,NULL,process_2,(void*)&Data[2]);		pthread_create(&p3,NULL,process_3,(void*)&Data[3]);			pthread_create(&p4,NULL,process_4,(void*)&Data[4]);		pthread_create(&p5,NULL,process_5,(void*)&Data[5]);		pthread_create(&p6,NULL,process_6,(void*)&Data[6]);		pthread_create(&p7,NULL,process_7,(void*)&Data[7]);		pthread_create(&p8,NULL,process_8,(void*)&Data[8]);		pthread_create(&p9,NULL,process_9,(void*)&Data[9]);        pthread_join(p0,NULL);		pthread_join(p1,NULL);		pthread_join(p2,NULL);		pthread_join(p3,NULL);        pthread_join(p4,NULL);		pthread_join(p5,NULL);		pthread_join(p6,NULL);		pthread_join(p7,NULL);		pthread_join(p8,NULL);		pthread_join(p9,NULL);        //==================输出数据后处理========================    ....}   

以上是我主程序的大概流程,由于pthread_create()创建子线程的时候给子线程函数的传入参数只有一个,所以当你需要给子函数传递多个函数参数或者需要传出参数的时候只能定义一个结构体,将所有输入和输出数据全部包装在结构体中,然后将结构体变量的引用传入子线程函数即可。在本例中,将所有输入输出参数全部包装在结构体InputData中,由于有十个子线程,为了避免不必要的麻烦,所以定义十个变量,用来输入和输出函数参数和返回值。

关于pthread_join( , )函数,它有两个参数,具体含义可以查阅其他博文。其作用是让主线程等待子线程结束,整个main函数是主线程,如果没有pthread_join这句代码,当主线程结束的时候,所有子线程也会被终止,而不管子线程是否已经结束,而大多数情况下,主线程会很快结束,然后创建的子线程还没有执行就随着主线程的结束而结束,所以一定要加一句等待子线程结束的代码。当然还有更简便的函数pthread_exit(),也可在所有线程的最后写上phread_exit(0)来让主线程等待所有子线程的结束。

phread.h中包含很多函数,在这里就不一一介绍,具体可参阅https://sourceware.org/pthreads-win32/manual/index.html

关于创建多少个线程才能效率更好的问题,网上和书上大部分推荐创建的线程数为CPU核数N 的2倍或者2*N+2个。所以这多线程和硬件关系特别大,如果是单核处理器,那完全不建议使用多线程,不然不但不会提高运行效率,反而线程之间的切换会占去很大一部分开销。

那么,我的实验运行速度提高了多少呐?? 答案是:没有提高。

让两台机子分别跑同一个数据集,一个采用上面的多线程方法,另一个串行执行每一组实验,经过对比,很悲伤的发现,串行的实验组跑得更快,当然和遗传算法本身也有很大的关系,每次一初始化不同,变异、交叉概率不同导致的找到解的时间就会不同,但是,这样的结果也是够让我伤心的了,搞了两天的多线程编程,查阅各种博客、去图书馆查资料,改程序,调程序,结果~~效果并不理想,也许是我的实验方法有问题,亦或是我的程序有问题,反正,多线程并没有加快我的实验进度。但是以后还是会尝试用多线程或者并行编程来加快遗传算法的实验速度的。