当前位置: 首页 > news >正文

C多线程编程- 近似求解π

本程序使用蒙特卡洛方法估算圆周率(π)。它首先创建了指定数量的线程,每个线程生成一个随机点并检查该点是否在单位圆内。基于这些线程的结果,程序计算在单位圆内的点的比例,并乘以4来估算π的值。为了对比,程序还直接在主线程中(没有并发)进行了相同的π估算过程(由于每次都是生成随机数,所以这个基准也没啥意义hh~)。最后,程序打印出两种方法得到的π值。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>struct arg_t {float x;float y;
};void *start(void *arg) {struct arg_t *ll = (struct arg_t *)arg;float x1 = ll->x;float y1 = ll->y;long M = 0;if (x1*x1 + y1*y1 <= 1.0) {M ++;}pthread_exit((void *)M);
}int main(int argc, char **argv) {if (argc < 2) {fprintf(stderr, "Please provide a number as an argument.\n");exit(1);}long N1 = atol(argv[1]);// printf("%ld\n", N1);pthread_t tids[N1];// pai(concurrency)for (long i = 0; i < N1; ++ i) {struct arg_t *arg = malloc(sizeof(struct arg_t));int x = rand();int y = rand();arg->x = 1.0*x / RAND_MAX;arg->y = 1.0*y / RAND_MAX;pthread_create(&tids[i], 0, start, arg);}void *res = 0;long M = 0;for (long i = 0; i < N1; ++ i) {pthread_join(tids[i], &res);M += (long)res;}printf("pai = %f\n", 4.0*M/N1);  // concurrency// pai(oracle)M = 0;for (long i = 0; i < N1; ++ i) {int x = rand();int y = rand();float x1 = 1.0*x / RAND_MAX;float y1 = 1.0*y / RAND_MAX;if (x1*x1 + y1*y1 <= 1.0) {M ++;}}printf("pai = %f\n", 4.0*M/N1);  // oraclepthread_exit(0);
}

测试一下上述程序:

majn@tiger:~$ ./pai 10000
pai = 3.171200
pai = 3.142000
majn@tiger:~$ ./pai 100000
Segmentation fault (core dumped)

分析Segmentation fault的原因:

在上面的程序中,为每个线程都动态分配了 arg_t 结构的内存,但在线程执行完毕后,这些内存并没有被释放。虽然这不是立即的问题,但长期这样会导致内存泄露。应该在线程函数中或 pthread_join 之后释放这些内存。

改进方案:

一种方法是在线程函数 start 的结尾释放它(插个眼hh~)。但由于在主函数中可能还需要访问这些结构,更安全的方法是在 pthread_join 之后释放这些动态分配的内存。

修改上述代码:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>struct arg_t {float x;float y;
};void *start(void *arg) {struct arg_t *ll = (struct arg_t *)arg;float x1 = ll->x;float y1 = ll->y;long M = 0;if (x1*x1 + y1*y1 <= 1.0) {M ++;}pthread_exit((void *)M);
}int main(int argc, char **argv) {if (argc < 2) {fprintf(stderr, "Please provide a number as an argument.\n");exit(1);}long N1 = atol(argv[1]);struct arg_t *args[N1];// printf("%ld\n", N1);pthread_t tids[N1];// pai(concurrency)for (long i = 0; i < N1; ++ i) {args[i] = malloc(sizeof(*args[i]));// struct arg_t *arg = malloc(sizeof(*arg));int x = rand();int y = rand();args[i]->x = 1.0*x / RAND_MAX;args[i]->y = 1.0*y / RAND_MAX;pthread_create(&tids[i], 0, start, args[i]);}void *res = 0;long M = 0;for (long i = 0; i < N1; ++ i) {pthread_join(tids[i], &res);M += (long)res;free(args[i]);}printf("pai = %f\n", 4.0*M/N1);  // concurrency// pai(oracle)M = 0;for (long i = 0; i < N1; ++ i) {int x = rand();int y = rand();float x1 = 1.0*x / RAND_MAX;float y1 = 1.0*y / RAND_MAX;if (x1*x1 + y1*y1 <= 1.0) {M ++;}}printf("pai = %f\n", 4.0*M/N1);  // oraclepthread_exit(0);
}

这样,每次线程执行完毕并被主线程收回后,对应的动态分配的内存都会被释放。

测试一下修改后的程序:

majn@tiger:~$ ./pai 100000
pai = 3.721760
pai = 3.137640
majn@tiger:~$ ./pai 1000000
Segmentation fault (core dumped)

好好好,这样玩儿是吧。

分析Segmentation fault的原因:

仔细观察上面的程序,我使用了一个固定大小的线程数组:pthread_t tids[N1];。对于大的 N1 值,这可能会导致栈溢出。在大多数系统上,默认的栈大小可能不足以容纳大量的 pthread_t 变量。

改进方案:

考虑动态分配线程ID数组的空间。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>struct arg_t {float x;float y;
};void *start(void *arg) {struct arg_t *ll = (struct arg_t *)arg;float x1 = ll->x;float y1 = ll->y;long M = 0;if (x1*x1 + y1*y1 <= 1.0) {M ++;}pthread_exit((void *)M);
}int main(int argc, char **argv) {if (argc < 2) {fprintf(stderr, "Please provide a number as an argument.\n");exit(1);}long N1 = atol(argv[1]);struct arg_t *args[N1];// printf("%ld\n", N1);pthread_t *tids = malloc(N1 * sizeof(pthread_t));// pai(concurrency)for (long i = 0; i < N1; ++ i) {args[i] = malloc(sizeof(*args[i]));// struct arg_t *arg = malloc(sizeof(*arg));int x = rand();int y = rand();args[i]->x = 1.0*x / RAND_MAX;args[i]->y = 1.0*y / RAND_MAX;pthread_create(&tids[i], 0, start, args[i]);}void *res = 0;long M = 0;for (long i = 0; i < N1; ++ i) {pthread_join(tids[i], &res);M += (long)res;free(args[i]);}printf("pai = %f\n", 4.0*M/N1);  // concurrency// pai(oracle)M = 0;for (long i = 0; i < N1; ++ i) {int x = rand();int y = rand();float x1 = 1.0*x / RAND_MAX;float y1 = 1.0*y / RAND_MAX;if (x1*x1 + y1*y1 <= 1.0) {M ++;}}printf("pai = %f\n", 4.0*M/N1);  // oraclepthread_exit(0);
}
majn@tiger:~$ ./pai 1000000
pai = 3.972176
pai = 3.142136
majn@tiger:~$ ./pai 10000000
Segmentation fault (core dumped)

???不玩儿了!!! 到此为止,我们把输入数据的规模N1从一开始最大接受10000扩大到现在最大接受1000000。

彩蛋: 要不然把为每个线程都动态分配的 arg_t 结构在线程函数 start 的结尾释放试一试?

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>struct arg_t {float x;float y;
};void *start(void *arg) {struct arg_t *ll = (struct arg_t *)arg;float x1 = ll->x;float y1 = ll->y;long M = 0;if (x1*x1 + y1*y1 <= 1.0) {M ++;}free(arg);pthread_exit((void *)M);
}int main(int argc, char **argv) {if (argc < 2) {fprintf(stderr, "Please provide a number as an argument.\n");exit(1);}long N1 = atol(argv[1]);// printf("%ld\n", N1);// pthread_t tids[N1];pthread_t *tids = malloc(N1 * sizeof(pthread_t));// pi(concurrency)for (long i = 0; i < N1; ++ i) {struct arg_t *arg = malloc(sizeof(struct arg_t));int x = rand();int y = rand();arg->x = 1.0*x / RAND_MAX;arg->y = 1.0*y / RAND_MAX;pthread_create(&tids[i], 0, start, arg);}void *res;long M = 0;for (long i = 0; i < N1; ++ i) {pthread_join(tids[i], &res);M += (long)res;}printf("pai = %f\n", 4.0*M/N1);  // concurrency// pi(oracle)M = 0;for (long i = 0; i < N1; ++ i) {int x = rand();int y = rand();float x1 = 1.0*x / RAND_MAX;float y1 = 1.0*y / RAND_MAX;if (x1*x1 + y1*y1 <= 1.0) {M ++;}}printf("pai = %f\n", 4.0*M/N1);  // oraclepthread_exit(0);
}
majn@tiger:~$ ./pai_init 10000000
pai = 3.997218
pai = 3.141381
majn@tiger:~$ ./pai_init 100000000
pai = 3.999722
pai = 3.141420
majn@tiger:~$ ./pai_init 1000000000
^C   // 时间太长了,不想等了

Amazing!!!怎么这么神奇?


注: 蒙特卡洛方法(Monte Carlo method)是一种通过随机抽样来获得数值解的统计方法。这个方法得名于摩纳哥的蒙特卡洛赌场,因为它大量使用随机性和概率。蒙特卡洛方法在物理学、工程学、经济学和许多其他领域都有广泛的应用。

关键概念和特点:

  1. 随机抽样:这是蒙特卡洛方法的核心。为了得到一个问题的数值解,这个方法使用随机数或更通常地说,使用伪随机数。

  2. 统计结果:通过对大量的随机样本进行统计分析,得到的是一个近似解,而不是确切的解。

  3. 精度与样本数量:通常,随着样本数量的增加,估算的精度也会提高。但是,为了使误差减少到原来的一半,样本数通常需要增加四倍。

  4. 应用:蒙特卡洛方法在多种应用中都非常有用,尤其是在问题的解析解很难得到或者不存在时。例如,它被用于估算复杂积分、求解难以解析的统计物理问题、进行金融市场模拟等。

  5. 示例 - 估算π:一个经典的应用是使用蒙特卡洛方法估算π的值。方法是这样的:随机投掷点到单位正方形内,统计落在单位圆内的点的数量。落在圆内的点数与总点数的比例,乘以4,就给出了π的近似值。

简而言之,蒙特卡洛方法是一种利用随机性来求解问题的技术,通过对大量样本的统计分析来获得结果。

http://www.lryc.cn/news/193866.html

相关文章:

  • YOLOV7量化第二步: 模型标定
  • 前端-uniapp-开发指南
  • Java集合类ArrayList的应用-杨辉三角的前n行
  • C语言-函数
  • 蓝桥杯 枚举算法 (c++)
  • Wordpress自定义小工具logo调用设置(可视化)
  • 面试常考数据结构:红黑树、B树、B+树各自适用的场景
  • Paddle GPU版本需要安装CUDA、CUDNN
  • MYSQL length函数
  • uniapp 在android手机上运行tab栏页面跳转问题
  • css3 hover效果
  • C语言char与short取反以及符号判断问题
  • Gpt-4多模态功能强势上线,景联文科技多模态数据采集标注服务等您来体验!
  • 【idea】 java: 找不到符号
  • Flink测试利器之DataGen初探 | 京东云技术团队
  • linux更换常用软件的默认缓存路径(.conda, .huggingface等)
  • Kafka消费者使用案例
  • SpringMVC全注解开发
  • 解决 android Cannot access ‘<init>‘: it is private in
  • 不容易解的题10.15
  • Megatron-LM GPT 源码分析(二) Sequence Parallel分析
  • DNA序列(DNA Consensus String, ACM/ICPC Seoul 2006, UVa1368) rust解法
  • 如何使用Jmeter进行http接口测试?
  • bash一行输入,多行回显demo脚本
  • IDEA spring-boot项目启动,无法加载或找到启动类问题解决
  • 【LeetCode刷题(数据结构与算法)】:完全二叉树的节点个数
  • 【代码随想录】算法训练营 第一天 第一章 数组 Part 1
  • 286_C++_定时器的其中一个操作,定时重载接口—startTimer循环执行回调(未完全)
  • 自动驾驶学习笔记(四)——变道绕行仿真
  • C++位图,布隆过滤器