使用 pthread 在 C 中生成随机数的最正确方法是什么

发布于 2024-12-10 06:39:10 字数 214 浏览 0 评论 0原文

我有多个线程同时运行,每个线程都必须生成随机数。我想了解是否有可遵循的模式,了解在主线程中使用 srand 初始化随机生成器是否正确,或者每个线程是否必须初始化自己的随机生成器。看来 rand/srand 尚未设计为与线程一起使用,我想知道如何一起处理线程和随机数。 谢谢

编辑:我需要纯随机数,但我也对生成用于测试目的的确定性序列感兴趣。我使用的是linux,但我更喜欢编写尽可能可移植的代码。

I have several threads running concurrently and each of them must generate random numbers. I want to understand if there is a pattern to follow, to understand if it is correct to initialize the random generator with srand in the main thread or if every thread must initialize its own random generator. It seems that rand/srand have not been designed to be used with threads and I'm wondering how I can deal with threads and random numbers together.
Thanks

EDIT: I need pure random numbers, but I'm also interested in generating a deterministic sequence for testing purposes. I'm on linux, but I prefer to write code as portable as possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

趴在窗边数星星i 2024-12-17 06:39:10

在 Linux 上,您可以使用 rand_r() 作为普通生成器,或者使用 drand48_r() 函数是一个更好的函数。两者都是 rand()drand48() 的线程安全替代品,通过采用由当前状态组成的单个参数,而不是使用全局状态。

关于您关于初始化的问题,上面的两个生成器都允许您在您想要的任何点播种,因此您不会被迫在生成线程之前播种它们。

On Linux you can use the rand_r() for a mediocre generator or the drand48_r() function for a much better one. Both are thread safe replacements for rand() and drand48(), by taking a single argument consisting of the current state, instead of using global state.

With regard to your question on initialization, both of the generators above allow you to seed at whatever point you desire, so you are not forced to seed them before spawning your threads.

—━☆沉默づ 2024-12-17 06:39:10

当使用线程并进行模拟等操作时,独立的随机生成器非常重要。首先,它们之间的依赖关系确实会使您的结果产生偏差,然后对随机生成器状态的访问控制机制很可能会减慢执行速度。

在 POSIX 系统上(您似乎在其中),有 *rand48 系列函数,其中 erand48nrand48jrand48 code> 将随机生成器的状态作为输入值。因此,您可以轻松地在每个线程中拥有独立的状态。您可以使用已知的数字(例如线程的数量)来初始化它们,并且您将获得可重现的随机数序列。或者您使用不可预测的内容(例如当前时间和时间)对其进行初始化。每次执行的序列数都不同。

When working with threads and doing e.g simulations or so it is very important that you have the random generators independent. First, dependencies between them can really bias your results and then mechanisms for access control to the state of the random generator will most likely slow down execution.

On POSIX systems (where you seem to be) there is the *rand48 family of functions where erand48, nrand48 and jrand48 take the state of the random generator as input values. So you can easily have independent states in each thread. Those you can initialize with a known number (e.g the number of your thread) and you'd have a reproducible sequence of random numbers. Or you initialize it with something non-predicable such as the current time & the number to have sequences that vary for each execution.

羁客 2024-12-17 06:39:10

在 Windows 上,您可以使用 rand_s() 函数,这是线程安全的。如果您已经在使用 Boost,则 boost::random 是有能力的(尽管我很欣赏这是标记为 C,而不是 C++)。

On Windows you can use the rand_s() function, which is thread safe. If you are already using Boost, then boost::random is competent (although I appreciate this is tagged C, not C++).

心的位置 2024-12-17 06:39:10

rand_r 是线程安全的,但也是可重入的。

下面的代码使用 xorshift 算法生成 uint128_t 伪随机数。

附加属性:

  • 共享可重入
  • 无锁
  • 线程安全
  • 超快
  • 种子,来自两种不同的熵源

uintx_types.h:

#ifndef UINTX_TYPES_H_INCLUDED
#define UINTX_TYPES_H_INCLUDED

#include <inttypes.h>
#include <ctype.h>

typedef __uint128_t     uint128_t;
typedef __uint64_t      uint64_t;

#define UINT128_C(hi, lo)   (((uint128_t)(hi) << 64) | (uint128_t)(lo))
#define UINT128_MIN         UINT128_C( 0x0000000000000000, 0x0000000000000000 )
#define UINT128_0           UINT128_MIN
#define UINT128_MAX         (~(UINT128_0) - 1) 

#endif // UINTX_TYPES_H_INCLUDED

lf.h:

#ifndef LF_H_INCLUDED
#define LF_H_INCLUDED

#define AAF(ADDR, VAL)          __sync_add_and_fetch((ADDR), (VAL))

#endif // LF_H_INCLUDED

rand.h:

#ifndef RAND_H_INCLUDED
#define RAND_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

#include "lf.h"
#include "uintx_types.h"


#define URANDOM     "/dev/random"   

void        srand_init(void);
uint128_t   rand_range_128(uint128_t min, uint128_t max);

#endif // RAND_H_INCLUDED

rand.c:

#include "rand.h"

uint64_t    r[2];

uint64_t xorshift64star(int index) 
{   
    uint64_t    x;

    x = r[index];
    x ^= x >> 12; // a
    x ^= x << 25; // b
    x ^= x >> 27; // c
    x = x * UINT64_C(2685821657736338717);
    return AAF(&r[index], x);
}

void srand_init(void)
{
    struct timespec ts;
    size_t          nbytes;
    ssize_t         bytes_read;
    int             fd;

    clock_gettime(CLOCK_REALTIME, &ts);
    r[0] = (uint64_t)(ts.tv_sec * 1.0e9 + ts.tv_nsec);
    xorshift64star(0);

    if ((fd = open(URANDOM, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH)) == -1)
    {
        r[1] = r[0] + 1;
        xorshift64star(1);
    }
    else
    {
        nbytes = sizeof(r[1]);
        bytes_read = read(fd, &r[1], nbytes);
        if ((bytes_read == 0) || (r[1] == 0ull))
        {
            r[1] = r[0] + 1;
            xorshift64star(1);
        }
        close(fd);
    }
}

uint64_t rand_64(void)
{
    return xorshift64star(0);
}

uint128_t rand_128(void) 
{
    uint128_t       r;

    r = xorshift64star(0);
    r = (r << 64) | xorshift64star(1);
    return r;
}


uint128_t rand_range_128(uint128_t min, uint128_t max)
{
    return (rand_128() % (max+1-min))+min;
}

test.c:

#define KEYS 1000

int main(int argc, char **argv)
{
    int             i;
    uint128_t       key;

    srand_init();

    for(i = 0; i <= KEYS; i++)
    {
        key = rand_range_128(UINT128_MIN, UINT128_MAX);
        printf("%016"PRIx64"%016"PRIx64"\n", (uint64_t)(key >> 64), (uint64_t)key);

    }
    return 0;
}

在 Linux 下使用 gcc(4.9.2) 编译。

rand_r is thread-safe but also is reentrant.

Code below generates uint128_t pseuso-random numbers using xorshift algorithm.

Additional properties:

  • shared-reentrant
  • lock-free
  • thread-safe
  • ultrafast
  • seeded from two variant sources of enthropy

uintx_types.h:

#ifndef UINTX_TYPES_H_INCLUDED
#define UINTX_TYPES_H_INCLUDED

#include <inttypes.h>
#include <ctype.h>

typedef __uint128_t     uint128_t;
typedef __uint64_t      uint64_t;

#define UINT128_C(hi, lo)   (((uint128_t)(hi) << 64) | (uint128_t)(lo))
#define UINT128_MIN         UINT128_C( 0x0000000000000000, 0x0000000000000000 )
#define UINT128_0           UINT128_MIN
#define UINT128_MAX         (~(UINT128_0) - 1) 

#endif // UINTX_TYPES_H_INCLUDED

lf.h:

#ifndef LF_H_INCLUDED
#define LF_H_INCLUDED

#define AAF(ADDR, VAL)          __sync_add_and_fetch((ADDR), (VAL))

#endif // LF_H_INCLUDED

rand.h:

#ifndef RAND_H_INCLUDED
#define RAND_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

#include "lf.h"
#include "uintx_types.h"


#define URANDOM     "/dev/random"   

void        srand_init(void);
uint128_t   rand_range_128(uint128_t min, uint128_t max);

#endif // RAND_H_INCLUDED

rand.c:

#include "rand.h"

uint64_t    r[2];

uint64_t xorshift64star(int index) 
{   
    uint64_t    x;

    x = r[index];
    x ^= x >> 12; // a
    x ^= x << 25; // b
    x ^= x >> 27; // c
    x = x * UINT64_C(2685821657736338717);
    return AAF(&r[index], x);
}

void srand_init(void)
{
    struct timespec ts;
    size_t          nbytes;
    ssize_t         bytes_read;
    int             fd;

    clock_gettime(CLOCK_REALTIME, &ts);
    r[0] = (uint64_t)(ts.tv_sec * 1.0e9 + ts.tv_nsec);
    xorshift64star(0);

    if ((fd = open(URANDOM, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH)) == -1)
    {
        r[1] = r[0] + 1;
        xorshift64star(1);
    }
    else
    {
        nbytes = sizeof(r[1]);
        bytes_read = read(fd, &r[1], nbytes);
        if ((bytes_read == 0) || (r[1] == 0ull))
        {
            r[1] = r[0] + 1;
            xorshift64star(1);
        }
        close(fd);
    }
}

uint64_t rand_64(void)
{
    return xorshift64star(0);
}

uint128_t rand_128(void) 
{
    uint128_t       r;

    r = xorshift64star(0);
    r = (r << 64) | xorshift64star(1);
    return r;
}


uint128_t rand_range_128(uint128_t min, uint128_t max)
{
    return (rand_128() % (max+1-min))+min;
}

test.c:

#define KEYS 1000

int main(int argc, char **argv)
{
    int             i;
    uint128_t       key;

    srand_init();

    for(i = 0; i <= KEYS; i++)
    {
        key = rand_range_128(UINT128_MIN, UINT128_MAX);
        printf("%016"PRIx64"%016"PRIx64"\n", (uint64_t)(key >> 64), (uint64_t)key);

    }
    return 0;
}

Compile with gcc(4.9.2) under Linux.

善良天后 2024-12-17 06:39:10

在 Linux 系统上,您可以使用如下函数:

size_t random_between_range( size_t min, size_t max ){
    unsigned short state[3];
    unsigned int seed = time(NULL) + (unsigned int) pthread_self();
    memcpy(state, &seed, sizeof(seed));
    return min +  nrand48(state) % (max - min );
}

这里我不得不说,我真的不知道该函数生成的数字是否符合正态分布,换句话说,如果该函数是 (min,max) 范围内的有效 RNG但至少让我编写了一个需要一些随机数的简单基准。

正如您所看到的,该函数利用 POSIX 线程 ID 来重新排列随机种子。这样做,每个线程都有自己的随机种子,而不是使用取决于 time(NULL) 的全局状态

On Linux systems you can use a function like:

size_t random_between_range( size_t min, size_t max ){
    unsigned short state[3];
    unsigned int seed = time(NULL) + (unsigned int) pthread_self();
    memcpy(state, &seed, sizeof(seed));
    return min +  nrand48(state) % (max - min );
}

Here I have to say that I don´t really know if numbers generated by this function fit to normal distribution in other words if this function is a valid RNG in the range (min,max) but at least worked for me to write a simple benchmark that required some random numbers.

As you can see, the function utilizes the POSIX thread id in order to re-arrange the random seed. Doing so, each thread has it own random seed instead of using global state depending on time(NULL)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文