2011-08-22 6 views
15

동일한 "전역 난수 생성기"가 모든 스레드에서 공유 되었습니까? 아니면 각 스레드가 자체 스레드를 가져 오나요?Haskell의 난수 생성기는 스레드로부터 안전합니까?

하나가 공유되면 스레드 안전성을 어떻게 보장 할 수 있습니까? "Monads" chapter of Real World Haskell에 설명 된 getStdGensetStdGen을 사용하는 접근 방식은 안전하지 않습니다.

각 스레드가 독립적 인 제너레이터를 가지고 있다면 두 스레드의 생성자가 연속적으로 빠르게 시작하여 다른 시드를 갖게됩니까? (예를 들어 씨앗이 초 단위 인 경우가 아니라 밀리 초가 괜찮을 수도 있습니다. 데이터 시간에서 밀리 초 단위로 분해하는 방법을 알지 못합니다.

답변

14

newStdGen이라는 함수가 있는데, 하나의 새로운 표준을 제공합니다. 그것이 불릴 때마다 Its implementationatomicModifyIORef을 사용하므로 스레드로부터 안전합니다.

newStdGen은 스레드 안전성면에서뿐만 아니라 let rnd = (fst . randomR (1,5)) <$> getStdGen in (==) <$> rnd <*> rnd과 같은 잠재적 단일 스레드 버그로부터 사용자를 보호합니다.

newStdGengetStdGen/setStdGen의 의미에 대해 생각해 보면 첫 번째 것은 매우 간단 할 수 있습니다. 새로운 표준을 얻습니다. 임의적 인 상태의 gen은 비 결정 론적으로 선택된다. 반면에 get/set 쌍을 사용하면 전역 프로그램 상태를 추상화 할 수 없으며 이는 여러 가지 이유로 좋지 않습니다.

+0

후드에서는 [FUZxxl의 대답] (http://stackoverflow.com/q/7153255/7153364#7153364)과 동일한 기술을 사용합니다. [ 'newStdGen' 문서] (http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:newStdGen)에는 "[it] [a] pplies가 현재 전역 임의 생성기로 분할되고 결과 중 하나를 업데이트하고 다른 하나를 반환합니다. "구현은 간단합니다. [atomicModifyIORef theStdGen split'] (http://hackage.haskell.org/packages/archive/ 임의/최신/doc/html/src/System-Random.html # newStdGen). –

10

getStdGen을 주 스레드에서 한 번만 사용한 다음 split 함수를 사용하여 새 생성자를 생성합니다. 나는 이렇게 할 것이다 :

발전기가 들어있는 MVar을 만든다. 스레드가 새 생성기를 필요로 할 때마다 MVar에서 현재 값을 가져오고 split을 호출하고 새 생성기를 다시 넣습니다. MVar의 기능으로 인해이 스레드는 안전해야합니다.

+1

실제로 [Rotor 's()]에 설명 된 ['newStdGen'] (http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:newStdGen) 대답] (http://stackoverflow.com/q/7153255/7155013#7155013)이 정확히 않습니다; 그것의 문서는 현재 글로벌 랜덤 생성기에 "split"을하고, 결과 중 하나를 업데이트하고, 다른 하나를 반환한다 "고 기술하고 있으며 그 구현은 단순히 ['atomicModifyIORef theStdGen split'] (http : //hackage.haskell.org/packages/archive/random/latest/doc/html/src/System-Random.html#newStdGen). –

+0

와우. 나는 그런 기능이 있는지 몰랐다. – fuz

3

자체적으로 getStdGensetStdGen은 특정 의미에서 스레드로부터 안전하지 않습니다. 두 개의 스레드를 가정 모두이 작업을 수행 :

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

다른 스레드가 setStdGen에 도달하기 전에 모두 스레드가 g <- getStdGen 줄을 실행하는 것은 가능은, 그러므로 그들은 모두 동일한 발전기를 얻을 수 있습니다. (내가 틀렸어?)

둘 다 동일한 버전의 발전기를 사용하고 동일한 기능에서 사용하면 동일한 '임의의'결과를 얻게됩니다. 따라서 난수 생성 및 멀티 스레딩을 처리 할 때는 좀 더 조심해야합니다. 많은 솔루션이 있습니다. 한 가지 염두에 떠오르는 것은 다른 스레드가 스레드로부터 안전한 방식으로 사용할 수있는 난수 스트림을 생성하는 하나의 전용 난수 생성기 스레드를 갖는 것입니다. FUZxxl이 말했듯이 발전기를 MVar에 두는 것은 아마도 가장 간단하고 가장 간단한 해결책 일 것입니다.

물론 코드를 검사하여 이 둘 이상의 스레드에서 임의의 숫자를 생성하도록해야합니다.

2

FUZxxl의 대답과 마찬가지로 split을 사용할 수 있습니다. 그러나 MVar를 사용하는 대신 forkIO을 호출 할 때마다 결과 생성기 중 하나에서 fork 된 스레드에 대한 IO 작업을 수행하고 다른 스레드는 원래 스레드로 남겨 둡니다. 이 방법으로 각 쓰레드는 고유 한 생성자를 갖습니다.

Dan Burton이 말한 것처럼 코드를 검사하고 여러 스레드에서 RNG가 필요한지 확인하십시오.

관련 문제