简体   繁体   English

种子随机数生成器与分叉一起工作的好方法是什么?

[英]What's a good way to seed a random number generator to work with forking?

Suppose I have a function generateId to generate random 64-bit integers. 假设我有一个函数generateId来生成随机的64位整数。 If I write generateId like this 如果我像这样写generateId

uint64_t generateId() {
  static thread_local std::mt19937_64 rng{std::random_device{}()};
  return rng();
}

Then rng won't get reseeded after a fork. 然后在叉子之后rng不会被重新接种。 Running 运行

int main() {
  std::cout << generateId() << "\n";
  if (fork() == 0) {
    std::cout << "child: " << generateId() << "\n";
  } else {
    std::cout << "parent: " << generateId() << "\n";
  }
  return 0;
}

Will print the same number for child and parent. 将为孩子和父母打印相同的号码。

Is there a way I can write generateId so that it reseeds for new processes but still remains performant. 有没有办法我可以编写generateId以便它为新进程重新种植,但仍然保持高性能。

Here's what I came up with: 这是我想出的:

class TlsRandomNumberGenerator {
 public:
   TlsRandomNumberGenerator() {
     pthread_atfork(nullptr, nullptr, OnFork);
   }

   static uint64_t GenerateId() {
     return random_number_generator_();
   }
 private:
  static thread_local std::mt19937_64 random_number_generator_;

  static void OnFork() {
    random_number_generator_.seed(std::random_device{}());
  }
};

thread_local std::mt19937_64 TlsRandomNumberGenerator::random_number_generator_{
    std::random_device{}()};

uint64_t generateId() {
  static TlsRandomNumberGenerator rng;
  return TlsRandomNumberGenerator::GenerateId();
}

It will work with forking but also doesn't have the overhead of calling getpid for every number generation, which depending on the version of libc you're using may or may not be cached so can involve a performance penalty. 它将与分叉一起使用,但也没有为每个数字生成调用getpid的开销,这取决于您正在使用的libc的版本可能会或可能不会被缓存,因此可能涉及性能损失。 See notes on getpid : 请参阅getpid注释:

From glibc version 2.3.4 up to and including version 2.24, the glibc wrapper function for getpid() cached PIDs, with the goal of avoiding additional system calls when a process calls getpid() repeatedly. 从glibc版本2.3.4到版本2.24,glibc包装函数用于getpid()缓存的PID,目的是在进程重复调用getpid()时避免额外的系统调用。

This should work: 这应该工作:

uint64_t generateId() {
   static pid_t mypid = 0;
   static std::unqiue_ptr<std::mt19937_64> rng;
   if( mypid != getpid() ) {
       rng.reset();
       mypid = getpid();
   }
   if( !rng )
      rng = std::make_unique<std::mt19937_64>(std::random_device{}());

   return (*rng)();
}

Note: this code is not thread safe, and I removed thread_local assuming you tried to solve fork() issue using it. 注意:此代码不是线程安全的,我删除了thread_local假设您尝试使用它解决fork()问题。 If multithreading involved then proper locking is ncesessary as well as considerations on problems using mutexes or non-blocking primitives with fork() 如果涉及多线程,那么正确的锁定是必要的,以及使用fork()使用mutexes或非阻塞原语的问题的考虑

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM