2011-08-22 43 views
15

Có cùng "trình tạo số ngẫu nhiên toàn cầu" được chia sẻ trên tất cả các chuỗi hoặc mỗi chuỗi có riêng không?Trình tạo số ngẫu nhiên trong Haskell thread-safe?

Nếu một tài khoản được chia sẻ, làm cách nào tôi có thể đảm bảo an toàn cho luồng? Cách tiếp cận sử dụng getStdGensetStdGen được mô tả trong "Monads" chapter of Real World Haskell không an toàn.

Nếu mỗi luồng có một trình tạo độc lập, các máy phát điện cho hai luồng bắt đầu liên tiếp nhanh có hạt giống khác nhau không? (Chẳng hạn, họ sẽ không, nếu hạt giống là thời gian tính bằng giây, nhưng mili giây có thể là OK. Tôi không thấy cách lấy thời gian với độ phân giải mili giây từ Data.Time.)

Trả lời

14

Có chức năng có tên newStdGen, cung cấp một hàm mới. gen mỗi khi nó được gọi. Its implementation sử dụng atomicModifyIORef và do đó an toàn chỉ.

newStdGen tốt hơn là get/setStdGen không chỉ về mặt an toàn luồng mà còn bảo vệ bạn khỏi các lỗi đơn luồng như thế này: let rnd = (fst . randomR (1,5)) <$> getStdGen in (==) <$> rnd <*> rnd.Ngoài ra, nếu bạn nghĩ về ngữ nghĩa của newStdGen so với getStdGen/setStdGen, những cái đầu tiên có thể rất đơn giản: bạn chỉ nhận được một tiêu chuẩn mới. gen ở trạng thái ngẫu nhiên, được chọn không xác định. Mặt khác, với cặp get/set, bạn không thể trừu tượng ra khỏi trạng thái chương trình toàn cục, điều này là xấu vì nhiều lý do.

+0

Lưu ý rằng dưới mui xe, điều này sử dụng kỹ thuật tương tự như câu trả lời của [FUZxxl] (http://stackoverflow.com/q/7153255/7153364#7153364); ['newStdGen''s documentation] (http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:newStdGen) nói rằng" [it] [a] pplies được chia thành trình tạo ngẫu nhiên toàn cầu hiện tại, cập nhật nó với một trong các kết quả và trả về kết quả còn lại, và việc thực thi nó chỉ đơn giản là ['atomicModifyIORef theStdGen split'] (http://hackage.haskell.org/packages/archive/ ngẫu nhiên/mới nhất/doc/html/src/System-Random.html # newStdGen). –

10

Tôi sẽ đề nghị bạn chỉ sử dụng getStdGen chỉ một lần (trong chuỗi chính) và sau đó sử dụng chức năng split để tạo ra các trình tạo mới. Tôi sẽ làm như sau:

Tạo MVar có chứa trình tạo. Bất cứ khi nào một luồng cần một trình tạo mới, nó sẽ lấy giá trị hiện tại ra khỏi số MVar, gọi split và đặt trình tạo mới trở lại. Do chức năng của một số MVar, điều này phải an toàn.

+1

Thực tế, hành động ['newStdGen'] (http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:newStdGen) được mô tả trong [Rotsor's answer] (http://stackoverflow.com/q/7153255/7155013#7155013) thực hiện chính xác điều này; tài liệu của nó nói rằng "[it] [a] pplies' split' thành trình tạo ngẫu nhiên toàn cục hiện tại, cập nhật nó với một trong các kết quả, và trả về kết quả còn lại "và việc thực hiện nó đơn giản chỉ là [' atomicModifyIORef theStdGen split'] (http : //hackage.haskell.org/packages/archive/random/latest/doc/html/src/System-Random.html#newStdGen). –

+0

Wow. Tôi không biết có một chức năng như vậy. – fuz

3

Bản thân, getStdGensetStdGen không phải là chủ đề an toàn theo một nghĩa nào đó. Giả sử hai chủ đề cả hai thực hiện hành động này:

do ... 
    g <- getStdGen 
    (v, g') <- someRandOperation g 
    setStdGen g' 

Có thể cho các chủ đề cho cả hai chạy dòng g <- getStdGen trước các chủ đề khác đạt setStdGen, do đó cả hai đều có thể nhận được các máy phát điện chính xác tương tự. (Tôi có sai không?)

Nếu cả hai đều lấy cùng một phiên bản của trình tạo và sử dụng cùng một chức năng, chúng sẽ nhận được kết quả "ngẫu nhiên" tương tự. Vì vậy, bạn cần phải cẩn thận hơn một chút khi xử lý việc tạo số ngẫu nhiên và đa luồng. Có rất nhiều giải pháp; một trong đó là để tâm trí là để có một chủ đề duy nhất số máy phát điện ngẫu nhiên duy nhất mà sản xuất một dòng số ngẫu nhiên mà các chủ đề khác có thể tiêu thụ một cách an toàn thread. Đặt máy phát điện trong một MVar, như FUZxxl gợi ý, có lẽ là giải pháp đơn giản và dễ hiểu nhất.

Tất nhiên tôi sẽ khuyến khích bạn kiểm tra mã của mình và đảm bảo rằng nó là cần thiết để tạo số ngẫu nhiên trong nhiều hơn một chuỗi.

2

Bạn có thể sử dụng split như trong câu trả lời của FUZxxl. Tuy nhiên, thay vì sử dụng một MVar, bất cứ khi nào bạn gọi forkIO, chỉ cần có hành động IO của bạn cho sợi chỉ được đóng trên một trong các trình tạo kết quả, và để nguyên một luồng khác với luồng ban đầu. Bằng cách này, mỗi luồng có bộ tạo riêng.

Như Dan Burton đã nói, hãy kiểm tra mã của bạn và xem liệu bạn có thực sự cần RNG trong nhiều chuỗi không.

Các vấn đề liên quan