算法如下:
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<time.h> int intervals=1000; //控制随机点的个数 int main() { clock_t start,delta; //用于统计程序运行时间 start=clock(); unsigned seed=time(NULL); int circle_points=0; int i; double pi; for(i=0;i<intervals*intervals;i++) { double rand_x=(double)rand_r(&seed)/RAND_MAX; double rand_y=(double)rand_r(&seed)/RAND_MAX; if((rand_x*rand_x+rand_y*rand_y)<=1) { circle_points++; } } pi=(4.0*circle_points)/i; printf("Cirlce points:%d, total_points:%d, the estimated PI is %lf\n",circle_points, i, pi); delta=clock()-start; printf("The time taken: %lf seconds\n",(double)delta/CLOCKS_PER_SEC); return 0; }可以看出来,这个算法较为简单,直接暴力求解了 π \pi π的值。关于生成随机数的函数为什么不用rand,而用rand_r,等到后面会做一个小实验来解释。 编译运行,求解的时间为:
代码如下:
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<time.h> void calculate_pi(int intervals) { int circle_points=0; int i; double pi; unsigned seed=time(NULL); for(i=0;i<intervals*intervals;i++) { double rand_x=(double)rand_r(&seed)/RAND_MAX; double rand_y=(double)rand_r(&seed)/RAND_MAX; if((rand_x*rand_x+rand_y*rand_y)<=1) { circle_points++; } } pi= (4.0*circle_points)/i; printf("Cirlce points:%d, total_points:%d, the estimated PI is %lf\n",circle_points, i, pi); } int main() { clock_t start,delta; double time_used; start=clock(); for(int i=0; i<10;i++) { calculate_pi(1000*(i+1)); } delta=clock()-start; printf("The time taken: %lf seconds\n",(double)delta/CLOCKS_PER_SEC); return 0;在1个CPU、三个计算核的资源下,结果如下: 在1个CPU、1个计算核的资源下:
##第三步:利用多线程多次计算 π \pi π值 代码如下:
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<time.h> #include<pthread.h> void* calculate_pi(void* arg) { int circle_points=0; int i; double pi; int intervals=*((int*)arg); //线程函数参数用于控制循环次数 unsigned seed=time(NULL); for(i=0;i<intervals*intervals;i++) { double rand_x=(double)rand_r(&seed)/RAND_MAX; // rand()函数不适用于并行计算 double rand_y=(double)rand_r(&seed)/RAND_MAX; if((rand_x*rand_x+rand_y*rand_y)<=1) { circle_points++; } } pi= (4.0*circle_points)/ i; printf("Cirlce points:%d, total_points:%d, the estimated PI is %lf\n",circle_points, i, pi); pthread_exit(0); } int main() { clock_t start,delta; double time_used; start=clock(); pthread_t calculate_pi_threads[10]; //用于存放10个线程函数的id int args[10]; //用于存放10个线程函数的参数 for(int i=0; i<10;i++) { args[i]=1000*(i+1); pthread_create(calculate_pi_threads+i, NULL, calculate_pi, args+i); } for(int i=0;i<10;i++) { pthread_join(calculate_pi_threads[i],NULL); } delta=clock()-start; printf("The time taken: %lf seconds\n",(double)delta/CLOCKS_PER_SEC); return 0; }其中函数pthread_exit为线程退出的显示函数,执行该条语句线程会退出。 此时由于运行速度和CPU的计算核心有关,所以需要设置CPU有关参数。以下数据为1个CPU,三个计算核的情况下得出。 注意到,在数据中运行的实时时间是大于用户态+内核态的运行时间的,这是因为实时时间是一个线程的完成时间,而user则是主线程完成的时间,这实际上也反映了在CPU有多个计算核的情况下的线程是并行处理的。 以下数据为1个CPU,1个计算核下得到的数据
从以上数据可以得到以下结论:
在多个核心情况下,利用多线程可以有效加速计算进度,但同时花销在线程调度的资源也会上升在只有一个核心的情况下,多线程和串行计算并没有什么区别,因为多线程这里变成了实际上的串行计算在给出的源代码中,可以发现这个注释 double rand_x=(double)rand_r(&seed)/RAND_MAX; // rand()函数不适用于并行计算 但是经过实验发现这个结论并不是绝对的,而是要根据环境而定义,在Windows环境下确实是这样的,而在Linux环境下是可以的。换句话说,rand函数在Windows下是非线程安全的,而在Linux环境下是线程安全的。实验结果如下:
Windows Linux:我猜测在Windows下不安全的原因是在于rand()函数的随机种子保存在PCB块中,由所有进程共享,而每个线程中的rand函数计数器变量则是由线程独享,而且都被初始化为相同值。这就导致了生成的随机数相同。而Linux下则没有这个缺陷,但是为了兼容性,还是建议在多线程编程时使用rand_r这个函数。