实验目的:
通过多线程编程(C语言)实践进一步理解操作系统中线程这一概念,熟悉linux系统操作,培养节省计算资源的意识。
预期效果:
使用linux系统,在通过多线程编程减少资源消耗的情况下,较精准地计算出圆周率的值。
实验背景:
蒙特卡罗法:蒙特卡罗法也称统计模拟法、统计试验法。是把概率现象作为研究对象的数值模拟方法。是按抽样调查法求取统计值来推定未知特性量的计算方法。蒙特卡罗是摩纳哥的著名赌城,该法为表明其随机抽样的本质而命名。故适用于对离散系统进行计算仿真试验。在计算仿真中,通过构造一个和系统性能相近似的概率模型,并在数字计算机上进行随机试验,可以模拟系统的随机特性。(摘自百度百科)
蒙特卡洛法计算圆周率方法推导:
正方形面积公式为
圆面积公式为
那么,对于一个正方形的内接圆来说,半径是正方形边长的,面积是其
。
所以,如果我们在一个正方形内随机生成若干个点,生成的点落在其内接圆内的概率即可被估计为为的值。
所以,我们可以写出计算圆周率的代码:
(实验环境为CentOS 7 64位)
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void calculate_pi(int times_root) {
// 变量初始化
unsigned int seed = time(NULL);
int points_in_circle = 0;
int points_in_square = 0;
// 生成随机点并判断其是否落在内接圆内
for (int i = 0; i < times_root*times_root; i++) {
double x = (double)rand_r(&seed)/RAND_MAX;
double y = (double)rand_r(&seed)/RAND_MAX;
if ((x*x) + (y*y) <= 1){
points_in_circle++;
}
points_in_square++;
}
// 输出结果
double pi = (double)(4.0*points_in_circle)/points_in_square;
printf("The estimated PI is %lf in %d times\n", pi, times_root*times_root);
}
int main() {
calculate_pi(1000);
return 0;
}
编译运行,测试成功。
实验思路:
如果我需要进行多次实验,以找出一个合理的值,就需要调用多次这个函数。随着times_root参数的变大以及调用次数的增加,消耗时间会逐渐增加。
for (int i=1; i<9; i++) {
calculate_pi(i*1000);
}
利用多线程,创建多个线程来执行计算,即可提升cpu时间利用率,从而提升计算效率。
所以最终的实验代码为:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
// 计算圆周率函数(根据pthread线程函数要求定义)。
void* calculate_pi(void* args) {
int times_root = *((int*)args);
unsigned int seed = time(NULL);
int points_in_circle = 0;
int points_in_square = 0;
for (int i = 0; i < times_root*times_root; i++) {
double x = (double)rand_r(&seed)/RAND_MAX;
double y = (double)rand_r(&seed)/RAND_MAX;
if ((x*x) + (y*y) <= 1){
points_in_circle++;
}
points_in_square++;
}
double pi = (double)(4.0*points_in_circle)/points_in_square;
printf("The estimated PI is %lf in %d times\n", pi, times_root*times_root);
}
int main() {
pthread_t calculate_pi_threads[8];
int args[8];
for (int i=1; i<9; i++) {
args[i-1] = i*1000;
pthread_create(calculate_pi_threads+i-1,NULL,calculate_pi,args+i-1);
}
for (int i=0; i<8; i++) {
pthread_join(calculate_pi_threads[i],NULL);
}
return 0;
}
测试结果:
可以看到实际运行时间为3.892秒,比直接在一个线程中执行代码要快很多。
本次实验的虚拟机配置仅有2个处理器内核,适当增加处理器内核可以进一步提升执行速度。