返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言中的rand()和rand_r()详解
  • 740
分享到

C语言中的rand()和rand_r()详解

2024-04-02 19:04:59 740人浏览 薄情痞子
摘要

目录背景rand()和rand_r()的区别rand()rand_r()总结背景 最近在学《并行程序设计导论》这门课,在做使用Pthreads并行化蒙特卡洛法估计 π \pi π的实

背景

最近在学《并行程序设计导论》这门课,在做使用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():

srand()和rand()配套一起使用,可以认为是进程只生成了一个随机数生成器,所有的线程共用这个随机数生成器。每调用一次rand(),rand()都会去修改这个随机数生成器的一些参数,比如说当前种子的值。对于单线程,这样是没有问题的,但是对于多线程而言,这显然会导致临界段问题,为了解决该问题,可能会使用互斥量等方法,下面假设多个线程同时使用rand()的时候使用互斥量来解决该临界段问题。如果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()。

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

请添加图片描述

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: C语言中的rand()和rand_r()详解

本文链接: https://lsjlt.com/news/160290.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言中的rand()和rand_r()详解
    目录背景rand()和rand_r()的区别rand()rand_r()总结背景 最近在学《并行程序设计导论》这门课,在做使用Pthreads并行化蒙特卡洛法估计 π \pi π的实...
    99+
    2024-04-02
  • C语言中随机数rand()函数详解
      在生活中很多场景下都需要产生随机数,比如抽奖,打牌,游戏等场景下就需要使用随机数。在C语言标准库函数里面有专门用来产生随机数的函数rand,它的函数原型如下:...
    99+
    2024-04-02
  • C语言的随机数rand()函数详解
    在生活中很多场景下都需要产生随机数,比如抽奖,打牌,游戏等场景下就需要使用随机数。在C语言标准库函数里面有专门用来产生随机数的函数rand,它的函数原型如下: int __cde...
    99+
    2024-04-02
  • 详解Go语言中rand(随机数)包的使用
    目录包"math/rand"随机数种子随机函数rand.Int()rand.Intn(n)实例其他随机函数按类型随机类指定随机范围类伪随机排列的切片生成标准正态分...
    99+
    2024-04-02
  • C语言rand和srand函数使用方法介绍
    目录前言随机数的本质重新播种生成一定范围内的随机数连续生成随机数前言 在实际编程中,我们经常需要生成随机数,例如,贪吃蛇游戏中在随机的位置出现食物,扑克牌游戏中随机发牌。 在C语言中...
    99+
    2023-02-11
    C语言rand和srand C语言rand方法 C语言srand方法
  • C语言中static和auto用法详解
    目录static的第一种用法:定义为静态变量static的第二种用法:有理说不清,直接代码见真知auto的用法:直接代码见真知总结static的第一种用法:定义为静态变量 何为静态变...
    99+
    2024-04-02
  • C语言中如何用rand()和srand()函数产生伪随机数
    这篇文章主要介绍“C语言中如何用rand()和srand()函数产生伪随机数”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言中如何用rand()和srand()函数产生伪随机数”文章能帮助大家解...
    99+
    2023-06-16
  • C语言的随机数rand()函数怎么用
    这篇文章主要为大家展示了“C语言的随机数rand()函数怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C语言的随机数rand()函数怎么用”这篇文章吧。在生活中很多场景下都需要产生随机数,...
    99+
    2023-06-29
  • C语言中的常量详解
    目录C语言中的常量字面常量#define定义的标识符常量枚举常量C语言中的常量 C编程中的常量是一些固定的值,它在整个程序运行过程中无法被改变。 字面常量 字面常量是直接写出的固定值...
    99+
    2024-04-02
  • C语言中怎么利用rand()和srand()函数产生伪随机数
    今天就跟大家聊聊有关C语言中怎么利用rand()和srand()函数产生伪随机数,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。rand()会返回一随机数值,范围在0至RAND_MAX...
    99+
    2023-06-17
  • C语言中调用汇编语言详解
    目录1.建立新工程2.改写程序3.总结1.建立新工程 首先点击Project里面的 New uVision Project 然后输入文件名,点击保存即可。 在你命名的project中...
    99+
    2024-04-02
  • C语言浮点函数中的modf和fmod详解
    modf函数可以提取出浮点数的整数部分和小数部分。fmod函数可以返回两个浮点数相除的余数。它们的函数原型如下: double __cdecl modf(double _X,do...
    99+
    2024-04-02
  • C语言中dlopen和dlsym的使用方式详解
    目录背景demo生产动态库调用dlopen总结背景 为了是不同的逻辑解耦,一般会把各个业务封装成动态库,然后主逻辑去调用各个插件。这里有个问题是,为什么以前我们都是通过include...
    99+
    2024-04-02
  • c语言中static和extern的用法详细解析
    一,static和extern:大工程下我们会碰到很多源文档。文档a.c复制代码 代码如下:static int i; //只在a文档中用int j;  &nbs...
    99+
    2022-11-15
    c语言 extern static
  • 详解C语言中++a和a++的不同之处
    c 语言中 ++a 和 a++ 有如下差异:++a 是前缀递增,先递增再返回,而 a++ 是后缀递增,先返回再递增。++a 返回递增后的值,而 a++ 返回递增前的值。根据所需的返回值类...
    99+
    2024-04-04
    c语言 语法规则 ++ a++
  • C语言 sockaddr和sockaddr_in案例详解
    struct sockaddr 和 struct sockaddr_in 这两个结构体用来处理网络通信的地址。 一、sockaddr sockaddr在...
    99+
    2024-04-02
  • C语言MultiByteToWideChar和WideCharToMultiByte案例详解
    目录注意:一、函数简单介绍( 1 ) MultiByteToWideChar()( 2 ) WideCharToMultiByte()二、使用方法( 1 ) 将多字节字符串...
    99+
    2024-04-02
  • C语言分支和循环详解
    目录前言一、什么是语句二、分支语句1.if语句2.switch语句三、循环语句1.while循环2.循环语句中的break与continue3.for循环总结前言 在本章,我会详述分...
    99+
    2024-04-02
  • C语言中递归和排列组合详解
    目录排列组合三大问题:1.打印n个数的全排列2.打印n个数中任意m个数的全排列3.打印n个数中任意m个数的组合完整代码如下:总结排列组合三大问题: 1.打印n个数的全排列2.打印n个...
    99+
    2024-04-02
  • C语言中scanf函数详解
    scanf函数是C语言中用于从标准输入流中读取数据的函数。它的原型如下:```cint scanf(const char *form...
    99+
    2023-09-13
    C语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作