播种随机数生成器以进行分叉的好方法是什么?

时间:2018-08-16 17:47:40

标签: c++ c++11 random

假设我有一个函数generateId来生成随机的64位整数。如果我这样写generateId

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

然后rng将不会在分叉后重新播种。正在运行

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

将为孩子和父母打印相同的数字。

有没有一种我可以写generateId的方式,以便它可以用于新进程,但仍保持高性能。

2 个答案:

答案 0 :(得分:1)

这是我想出的:

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();
}

它可以与派生一起使用,但也没有每个数字生成都调用getpid的开销,这取决于您所使用的libc版本可能会或可能不会被缓存,因此可能会影响性能罚款。请参见getpid上的注释:

  

从glibc 2.3.4版到2.24版(包括2.24版),glibc          getpid()缓存的PID的包装函数,目的是避免          进程重复调用getpid()时,会进行其他系统调用。

答案 1 :(得分:0)

这应该有效:

Option Explicit

Sub FillColumnAccordingToFilteredRows()

Dim bidNum As String
Dim bidFull As String

Dim PriceLObj As ListObject

bidNum = "123" 'sample number
bidFull = "Bid-" + bidNum + ".csv"

Set PriceLObj = ActiveSheet.ListObjects("PriceExp") ' set the ListObject

PriceLObj.Range.AutoFilter Field:=1, Criteria1:=bidFull  ' filters data by Bid #

Dim VisRng As Range, VisArea As Range, VisRow As Range
Dim ColBRng As Range, ColCRng As Range

' get the visible range of the filteres list object
Set VisRng = PriceLObj.Range.SpecialCells(xlCellTypeVisible)

' === set column B and column C Range ===
For Each VisArea In VisRng.Areas ' loop through areas in non-contigous filtered range

    ' loop through rows in area
    For Each VisRow In VisRng.Rows
        If VisRow.Row <> 1 Then ' not header row
            ' set and Merge ranges for column B range
            If Not ColBRng Is Nothing Then
                Set ColBRng = Application.Union(ColBRng, Range("B" & VisRow.Row))
            Else
                Set ColBRng = Range("B" & VisRow.Row)
            End If

            ' set and Merge ranges for column C range
            If Not ColCRng Is Nothing Then
                Set ColCRng = Application.Union(ColCRng, Range("C" & VisRow.Row))
            Else
                Set ColCRng = Range("C" & VisRow.Row)
            End If
        End If
    Next VisRow
Next VisArea

' make sure column B range is not empty (otherwise will pop an error)
If Not ColBRng Is Nothing Then ColBRng.Value2 = "sample name"

' make sure column C range is not empty (otherwise will pop an error)
If Not ColCRng Is Nothing Then ColCRng.Value2 = "sample state"

End Sub

注意:此代码不是线程安全的,并且假设您尝试使用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)(); } 解决thread_local问题,因此我删除了fork()。如果涉及多线程,则必须进行适当的锁定以及使用mutexes或使用fork()的非阻塞原语的问题的考虑因素