2011-10-17 2 views
1

私は、スレッド化と並行性に関してかなり不慣れです。これを改善するために、私は現在、F#でランダム検索アルゴリズムを実装するのに面白い作業をしています。私はSystem.Randomクラスのラッパーを作成しました。既存のC#のアイデアに従っていますが、どうやって単体テストを始めるのか分からないので、経験豊かな人たちが何を言いたいのか聞いてみたいこれは適切なスレッドセーフなランダムラッパーですか?

open System 
open System.Threading 

type Probability() = 

    static let seedGenerator = new Random() 

    let localGenerator = 
     new ThreadLocal<Random>(
     fun _ -> 
      lock seedGenerator (
       fun _ -> 
        let seed = seedGenerator.Next() 
        new Random(seed))) 

    member this.Draw() = 
     localGenerator.Value.NextDouble() 

これが何をするかの私の理解::、およびF#構文に起因するか、誤解をスレッドのいずれかの私のコードで明らかに傷や改善点がある場合にThreadLocalは、例えば、各スレッドは独自のインスタンスを確実に受け取ることができますランダム、一般的な静的ランダムによって提供されるランダムなシードを持ちます。こうすることで、クラスのインスタンスが複数作成されても、それらのインスタンスが独自のシードを受け取り、「重複した」ランダムシーケンスの問題を回避できます。ロックによって、2つのスレッドが同じシードを取得することはありません。

この見た目は正しいですか?明らかな問題はありますか?

答えて

5

私はあなたのアプローチはかなり合理的だと思う - ThreadLocalを使用すると、あなたにRandomへの安全なアクセスを提供し、種を提供するために、マスター乱数ジェネレータを使用すると、あなたがで複数のスレッドからアクセスする場合でも、ランダムな値を取得しますことを意味し同様の時間。暗号の意味でランダムではないかもしれませんが、他のほとんどのアプリケーションでは問題ありません。

テストに関しては、これは非常に難しいです。 Randomが壊れた場合、常に0が返されますが、これは経験的な経験に過ぎず、どれくらいの期間アクセス不能にしておく必要があるかについては言い難いです。私が示唆できる最も良いことは、いくつかの簡単なランダム性テスト(some simple ones are on WikiPedia)を実装し、ループ内の複数のスレッドから型にアクセスすることです - これは毎回失敗しないので、かなり悪いテストです。

さらに、この動作をカプセル化するためにtypeを使用する必要はありません。これも関数として書くことができます:

open System 
open System.Threading 

module Probability = 

    let Draw = 
    // Create master seed generator and thread local value 
    let seedGenerator = new Random() 
    let localGenerator = new ThreadLocal<Random>(fun _ -> 
     lock seedGenerator (fun _ -> 
     let seed = seedGenerator.Next() 
     new Random(seed))) 
    // Return function that uses thread local random generator 
    fun() -> 
     localGenerator.Value.NextDouble() 
1

パフォーマンスのボトルネックがない限り、私は考えて

let rand = new Random() 

let rnext() = 
    lock rand (
     fun() -> 
      rand.next()) 

のようなものを理解することが容易になりますが、私はあなたの方法は問題ないはずだと思います。

+0

すぐに連続していくつかのオブジェクトを生成する場合、このようなコードの問題があります。それらのすべてが同じ種を持つからです。あるいは、それらがグローバルであることを意味しましたか? – svick

+0

@svick - Pointはランダムなスタック全体を生成する理由でした - これは複数のスレッドからアクセスできる単なるランダムなものです。はるかに簡単で、乱数の数が必要ない限り、それはうまくいくはずです。もし、ランドがボトルネックであれば、別のジェネレータを使って速度を上げるほうがよいでしょう。 –

+0

高価なビットはすべての乱数のロックです。 –

0

あなたが本当にOOのアプローチをしたいのであれば、あなたのコードはうまくいくかもしれません(私はOOを理解するにはあまりスマートではないので、それは大丈夫です)。あなたは「Porbability」タイプのオブジェクトと同じくらいを生成して、さまざまなスレッドに配布する機能probabilityGeneratorを使用することができます。ここ

type Probability = { Draw : unit -> int } 

let probabilityGenerator (n:int) = 
    let rnd = new Random() 
    Seq.init n (fun _ -> new Random(rnd.Next())) 
    |> Seq.map (fun r -> { Draw = fun() -> r.Next() }) 
    |> Seq.toList 

:しかし、場合には、あなたはそれが何かのように単純になり、機能の道を行きたいですそれを並行して処理することができます。 ここで重要なことは、コアタイプ、つまり確率でロックなどを導入していないことです。つまり、消費者がスレッド間でどのように配布するかは、消費者の責任となります。

+0

私は 'Seq.toList'の代わりに' Seq.toArray'を使うので、呼び出し側は配列インデックスをスレッドIDとして使うことができます。 – ildjarn

+0

この関数は、同じスレッド(呼び出し元のスレッド)のn個のアイテムを生成します – Ankur

+0

はい、配列は複数のスレッドからの読み取り専用アクセスに対して安全です。したがって、このソリューションは単一スレッドまたはマルチスレッドのソリューションと同じように使用できます。 – ildjarn

3

これは間違っています。なぜ、シングルトンを使用するだけではなく(今までに1つのランダムインスタンスを作成してロックするのはなぜですか?

実際のランダム性が問題になる場合は、RNGCryptoServiceProvider(スレッドセーフです)を参照してください。

+0

私はいつもよりランダムな分布のためにRNGCryptoServiceProviderを使う傾向があります。 – 7sharp9

+0

回避しようとする問題は、同じシードを持つ2つの別々のランダムインスタンスを持つことです。 –

関連する問題