rand和rand_r

  • 背景
  • rand()和rand_r()的区别
    • rand()
    • rand_r()

背景

最近在学《并行程序设计导论》这门课,在做使用Pthreads并行化蒙特卡洛法估计π\piπ的实验时遇到了一个问题,使用多线程反而要比单线程要慢很多,输出如下所示

可以看到,使用一个线程时程序运行只需要2.89031秒,但是使用两个线程时运行时间竟然达到了9.14698秒。
最终发现了问题所在:每个线程在执行下面的函数时,生成随机数使用了rand()函数,就是这个函数的使用才导致多线程运行时所需要的时间反而更长

long long total_times_in_cycle;
long long local_number_toss;
pthread_mutex_t times_in_cycle_mutex = PTHREAD_MUTEX_INITIALIZER;
void* do_Monte_Carlo_simulation(void* my_rank){double offset = RAND_MAX / (double)2;long long times_in_cycle = 0;long long i;for(i = 0; i < local_number_toss; ++i){double x = rand() / offset - 1;double y = rand() / offset - 1;if(x*x + y*y < 1){times_in_cycle++;}}pthread_mutex_lock(&times_in_cycle_mutex);total_times_in_cycle += times_in_cycle;pthread_mutex_unlock(&times_in_cycle_mutex);
}

rand()和rand_r()的区别

权威的解释请参考Linux的官方文档
这里主要说说我个人的理解。

rand()

对于rand():

  1. srand()和rand()配套一起使用,可以认为是进程只生成了一个随机数生成器,所有的线程共用这个随机数生成器。每调用一次rand(),rand()都会去修改这个随机数生成器的一些参数,比如说当前种子的值。
  2. 对于单线程,这样是没有问题的,但是对于多线程而言,这显然会导致临界段问题,为了解决该问题,可能会使用互斥量等方法,下面假设多个线程同时使用rand()的时候使用互斥量来解决该临界段问题。
  3. 如果rand()调用的次数不多,多线程也问题不大,但是对于上述蒙特卡洛法估计π\piπ的程序,需要调用很多次rand(),这可能会导致每个线程频繁地对临界段进行上锁和解锁,而临界段被上锁后,其他线程无法完成rand()的调用从而被阻塞,这样会导致效率十分低下,因此会出现使用多线程反而程序运行更慢的问题。

srand()和rand()配套一起使用,可以认为是进程只生成了一个随机数生成器,所有的线程共用这个随机数生成器 这点可以用下面的程序进行验证,该程序从命令行获取要生成随机数的数量以及线程的数量,每个线程负责生成 随机数的数量/线程的数量 个随机数,随机数种子设为0.

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>int generate_rand_count;
int thread_count;// 被线程执行的函数
void* thread_func(void* my_rank){int i;int local_rand_count = generate_rand_count / thread_count;for(i = 0; i < local_rand_count; ++i){printf("%d ", rand());}
}int main(int argc, char* argv[]){pthread_t* all_threads_id;// 从命令行获取要生成的随机数的数量以及这些随机数由多少个线程来生成generate_rand_count = strtol(argv[1], NULL, 10);thread_count = strtol(argv[2], NULL, 10);all_threads_id = malloc(sizeof(pthread_t) * thread_count);// 设置随机数种子srand(0);// 创建线程int i;for(i = 0; i < thread_count; ++i){pthread_create(&all_threads_id[i], NULL, thread_func, (void*) i);}for(i = 0; i < thread_count; ++i){pthread_join(all_threads_id[i], NULL);}printf("\n");free(all_threads_id);return 0;
}

执行结果如下所示

可以看到,无论使用一个线程生成10个随机数,还是说使用两个线程来生成10个随机数,它们生成的这10个随机数是一样的,这也就验证了 所有的线程共用一个随机数生成器 的观点

rand_r()

rand_r()的声明如下所示

int rand_r(unsigned int *seedp);

每次使用rand_r()的时候需要传给该函数一个随机数种子的指针,为了解决蒙特卡洛方法估计π\piπ中出现的问题,使用如下的代码:

long long total_times_in_cycle;
long long local_number_toss;
pthread_mutex_t times_in_cycle_mutex = PTHREAD_MUTEX_INITIALIZER;
void* do_Monte_Carlo_simulation(void* my_rank){unsigned int local_seed = time(NULL);double offset = RAND_MAX / (double)2;long long times_in_cycle = 0;long long i;for(i = 0; i < local_number_toss; ++i){double x = rand_r(&local_seed) / offset - 1;double y = rand_r(&local_seed) / offset - 1;if(x*x + y*y < 1){times_in_cycle++;}}pthread_mutex_lock(&times_in_cycle_mutex);total_times_in_cycle += times_in_cycle;pthread_mutex_unlock(&times_in_cycle_mutex);
}

每个线程执行函数do_Monte_Carlo_simulation()
使用上面的代码后,相当于每个线程生成了它自己独有的随机数生成器,这样就不会有临界段问题,从而解决了多线程运行时所需要的时间反而更长这个问题,下面为更改后程序的输出:

rand()和rand_r()的区别相关推荐

  1. numpy中rand与randn的区别

    rand是随机生成值在0-1之间的函数: randn是随机生成均值为0,方差为1的正态分布上的数值. 通过一张图可以清晰看到区别 %matplotlib inline import matplotli ...

  2. 今天说说rand和randi的区别

    首先 rand 和 randi 都是用来生成随机数的命令 区别:1.rand是产生均匀分布的随机数:randi是产生均匀分布的随机整数 2.rand产生的是随机数,randi产生的是伪随机数(随机数和 ...

  3. Matlab常用函数:rand,randi和randn区别

    简单介绍 1.rand()函数 功能:rand() 生成一个均匀分布的伪随机数,分布在(0~1)之间. 主要语法: rand(m,n)生成m行n列的均匀分布的伪随机数.rand(m,n,'double ...

  4. 随机数函数rand()和srand()的区别

    引子 相信大家对于rand()函数并不陌生,我们常用它来生成伪随机数,但是为什么有时候我们生成的随机数并不符合预期呢?或者说,为什么有时候我们生成的随机数并不随机?如何有效地生成伪随机数呢? rand ...

  5. c语言随机数表,C语言随机数

    头文件:#include 函数原型:long int random(void); void srandom(unsigned int seed); char* initstate(unsigned i ...

  6. random函数 c语言,C/C++ 中rand()和random()函数(示例代码)

    nt rand(void); 返回一个随机数0 ~ pow(2, sizeof(int))-1 long int random(void); 返回一个随机数0 ~ pow(2, sizeof(long ...

  7. Linux——获取系统信息

    目录 一.关于时间的概念 二.linux系统中的时间 三.时间相关API实战 五.proc文件系统介绍 六.proc文件系统的使用 一.关于时间的概念 1.GMT时间 (1)GMT是格林尼治时间,也就 ...

  8. access汇总含义_2020最新大厂内部 PHP 高级工程师面试题汇总(二)

    51.数据库中的存放了用户 ID, 扣费很多行,redis 中存放的是用户的钱包,现在要写一个脚本,将数据库中的扣费记录同步到 redis 中,每 5 分钟执行一次.请问要考虑哪些问题? 思路:生产者 ...

  9. php知识点_PHP那些琐碎的知识点(整理)

    PHP不会检查单引号 '' 字符串中变量内插或(几乎)任何转义序列,所以采用单引号这种方式来定义字符串相当简单快捷.但是,双引号 "" 则不然,php会检查字符串中的变量或者转义序 ...

  10. 随机过程第1讲——泊松过程的模拟与检验

    定义:泊松过程,简单的来说,满足下列三条件的随机过程X={X(t),t≥0}:①P(X(0)=0)=1.②不相交区间上增量相互独立,即对一切0≤t1<t2<-<tn,X(t1),X( ...

最新文章

  1. 360推两款无刘海新机,主打安全和AI,起售价1199
  2. python的sklearn示例_KNN sklearn python实现小示例
  3. sql--找含有制定字符列的表
  4. 【采用】如何搭建反欺诈策略与模型
  5. jpa一级缓存和二级缓存_了解一级JPA缓存
  6. Android图片上传和下载,android 上传/下载 图片
  7. twig php代碼,有没有办法在wordpress的.twig文件中编写php代码?我试图使用.twig模板文件中的表单值发送邮件...
  8. 切割日志(mysql,nginx,php tomcat)使用logrotate
  9. Spark开发入门与实践(一)
  10. vbs编程中 on error resume next的意思
  11. 无线渗透(四)WPA攻击
  12. 微信小程序云存储(文件上传到云端)
  13. 感应加热电源-谐振移相-感性移相
  14. 关于WGAN的学习总结——Lipschitz约束与正则化
  15. 从此告别PPT制作的烦恼:ChatGPT和MindShow帮你快速完成
  16. gke下载_我们如何在GKE上升级Kubernetes
  17. ArcSDE版本学习总结
  18. nodejs入门之数据爬虫
  19. 美多商城项目发送短信优化
  20. 大连在线旅游网站推荐-找驴网

热门文章

  1. 人人都能看懂的 AI 入门课
  2. SEO入门到精通只需学会这10个关键技巧
  3. seo网站优化技巧_新网站的10个SEO技巧
  4. Android 的 getIntExtra(name, defaultValue)里 defaultValue是什么意思
  5. java生成png热力图_热力图与原始图像融合
  6. 打印html并去掉页眉页脚
  7. 《轩辕剑4苍之涛》另一攻略 (3)
  8. 【Pigeon源码阅读】服务调用请求流程解析(六)
  9. rk3288 android 6.0固件,Firefly-RK3288主板烧写官方Android固件起不来
  10. Gateway统一网关(2021-11-14)