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中写多线程程序了。

第二步.编程

下面是我的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#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个。所以这多线程和硬件关系特别大,如果是单核处理器,那完全不建议使用多线程,不然不但不会提高运行效率,反而线程之间的切换会占去很大一部分开销。

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

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


C语言使用pthread多线程编程(windows系统)二

我们进行多线程编程,可以有多种选择,可以使用WindowsAPI,如果你在使用GTK,也可以使用GTK实现了的线程库,如果你想让你的程序有更多的移植性你最好是选择POSIX中的Pthread函数库,我的程序是在Linux下写的,所以我使用了Pthread库(是不是很伤心,我知道有不少人期待的是WindowsAPI的,好吧,有机会以后再讲那个,现在先把这一系列专题写完 ^_^)

如果你用的是LINUX/UNIX/MacOSX,那么我们已经可以开始了,如果你用的是WINDOWS,那么你需要从网站上下载PTHREAD的WINDOWS开发包,所幸他非常的小。网站地址是http://sourceware.org/pthreads-win32/

先来看一个基本的例子:
 程序代码
#include <pthread.h>
#include <iostream>

using namespace std;

void* tprocess1(void* args){
         while(1){
                 cout << "tprocess1" << endl;
         }
         return NULL;
}

void* tprocess2(void* args){
         while(1){
                 cout << "tprocess2" << endl;
         }
         return NULL;
}

int main(){
         pthread_t t1;
         pthread_t t2;
         pthread_create(&t1,NULL,tprocess1,NULL);
         pthread_create(&t2,NULL,tprocess2,NULL);
         pthread_join(t1,NULL);
         return 0;
}

在上面的例子中,我们首先加入了pthread.h文件包含,这是所以pthread多线程程序所必须的,接着是iostream我们进行输入输出时要用到,接着就是两个函数的定义,这和普通的函数没有什么区别,之所以写成的
 程序代码
void* tprocess1(void* args)
这样的形式,完全是为了迎合pthread_create函数的参数类型,你也可以不这样定义,只要在调用pthread_create创建线程的时候强制转换一下指针类型就可以了。

这两个函数将被用做线程的执行体,也就是说在两个线程里同时运行这两个函数。

现在我们来看main函数,和pthread有关的调用都在这里了。
pthread_t是线程结构,用来保存线程相关数据,你也可以理解为是线程类型,声明一个线程对象(变量)。
 程序代码
         pthread_t t1;
         pthread_t t2;
这里我们声明了两个线程变量t1,t2

 程序代码
         pthread_create(&t1,NULL,tprocess1,NULL);
         pthread_create(&t2,NULL,tprocess2,NULL);
这两句非常重要,pthread_create用来创建线程并启动,他的原型是
 程序代码
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
我们可以知道第一个参数是线程指针,第二参数是线程属性指针,线程属性pthread_attr_t用来指定线程优先级等属性,一般的情况下,我们没有必要修改,使用默认属性来构造线程,所以这里一般取NULL,我们也是这样做的,第三个参数是一个函数指针(函数指针?什么东西,没听说过啊?……巨晕,好嘛,你复习一下C或是C++指针那部分吧)就是线程要执行的代码,这里我们分别要执行tprocess1 tprocess2就写成了上面的样子,这里这个函数指针的类型定义是返回一个空类型指针,接收一个空类型指针参数的函数指针,如果你的函数不是这个定义,那就可以直接转化一下就可以了。

写完这两行代码,两个线程就已经执行起来了,但是如果你省略了
 程序代码
         pthread_join(t1,NULL);
然后尝试编译运行程序的时候你会发现程序似乎什么也没干就退出了,是的,那是因为程序的主线程退出的时候操作系统会关闭应用程序使用的所有资源,包括线程……所以在main函数结束前我们得想办法让程序停下来,pthread_join方法的功能就是等待线程结束,要等的线程就是第一个参数,程序会在这个地方停下来,直到线程结束,第二个参数用来接受线程函数的返回值,是void**类型的指针,如果没有返回值,就直接设为NULL吧。

程序写好了,我们怎么编译运行它呢?
如果你使用的是Linux:
在终端里输入
g++ thread.cpp -othread -lpthread
./thread
就可以完成程序的编译及运行

如果你用的是VC:
在工程属性里加入开发包里的几个库文件
把那几个DLL文件放到你的工程路径里,也就是程序运行时候的工作路径,这个在VC6和2005里似乎不太一样,如果你不确定,那就直接放到SYSTEM32里吧。。。
下面的工作就非常简单了
点运行,提示编译,就确定,好了,结果出来了。。。

是不是感觉多线程如此的简单,短短几行代码就搞定了,我想你已经可以写出一个简单的多线程程序了吧,呵呵,其实问题没有这么简单,多线程我们还要面对线程同步的问题,我会在下一个专题里给大家讲到。  

出处:http://www.cnblogs.com/lovko/archive/2009/01/15/1376032.html


猜你喜欢

转载自blog.csdn.net/alisa_xf/article/details/79159037
今日推荐