2011-07-16 4 views
0

私はC#で新しく、レーストラックシミュレータを作成していますが、私は現在コードを実行するときにいくつかの問題に遭遇しています。私は4つのグレイハウンドオブジェクトの配列を持っています。私がform1.csでGreyhound.Runを呼び出すと、Runメソッドに "MessageBox.Show(" Distance "+ distance)各グレイハウンドが移動するはずですが、グレイハウンドのすべてが同じ距離を移動してしまいます。これはあなたがnew Random()Run()方法はelseブロックを打つたびに呼び出しているためですC#newb質問から "ヘッドスタートC#グレイハウンドラボ"

namespace Race 
{ 
    class Greyhound 
    { 
     public int StartingPosition; 
     public int RacetrackLength; 
     public PictureBox MyPictureBox = null; 
     public int Location = 0; 
     public Random Randomizer; 

     public bool Run() 
     { 
      Point p = MyPictureBox.Location; 
      if (p.X + MyPictureBox.Width >= RacetrackLength) 
      { 
       //TakeStartingPostion(); 
       return true; 

      } 
      else 
      { 
       Randomizer = new Random(); 
       int distance = Randomizer.Next(100); 
       MessageBox.Show("Distance is " + distance); 
       p.X += distance; 
       MyPictureBox.Location = p; 
       return false; 
      } 

     } 

     public void TakeStartingPostion() 
     { 
      Point P = MyPictureBox.Location; 
      P.X = StartingPosition; 
      MyPictureBox.Location = P; 
     } 
    } 
} 


namespace Race 
{ 
    public partial class Form1 : Form 
    { 
     Guy[] guys = new Guy[3]; 
     Greyhound[] hounds = new Greyhound[4]; 

     public Form1() 
     { 
      InitializeComponent(); 

      hounds[0] = new Greyhound() 
      { 
       StartingPosition = 12, 
       MyPictureBox = GreyHound1, 
       RacetrackLength = 636 
      }; 

      hounds[1] = new Greyhound() 
      { 
       StartingPosition = 12, 
       MyPictureBox = GreyHound2, 
       RacetrackLength = 636 
      }; 

      hounds[2] = new Greyhound() 
      { 
       StartingPosition = 12, 
       MyPictureBox = GreyHound3, 
       RacetrackLength = 636 
      }; 

      hounds[3] = new Greyhound() 
      { 
       StartingPosition = 12, 
       MyPictureBox = GreyHound4, 
       RacetrackLength = 636 
      }; 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      for (int i = 0; i < hounds.Length; i++) 
      { 

       hounds[i].Run(); 
      } 

     } 
    } 
} 

答えて

1

が起こっている理由を私は理解していません。デフォルトのRandomコンストラクタは、現在の時刻に基づいて擬似乱数ジェネレータを初期化します。中断がない場合、4つの方法はすべて「同じ時刻」に実行されるため、同じ乱数が吐き出されます。 (。パラメータとしてシードを取るRandomコンストラクタを使用することにより)のいずれか、この問題を解決のみ単一Randomを作成するか、あるいは、それぞれに異なるシードを使用するには、このような

何かが働くだろう:

public class Greyhound 
{ 
    public static Random randomizer = new Random(); 

    // ... In the run method ... 
    int distance = Greyhound.randomizer.Next(100); 

} 

更新:Grooが指摘するように、あなたが実際には複数のスレッドからNext()を呼んでいた場合、私が示したコードは、スレッドセーフではありません。これはコードでは当てはまりませんが、後ではなく以前のような問題を認識することは良い考えです。この(潜在的な)問題に対する一般的な解決策はそうのように、lockNext()への呼び出しを囲むようにある:

// ... After the declaration of randomizer ... 
private static object randomLock = new object(); 

// ... New Next() call... 
lock (randomLock) 
    Greyhound.randomizer.Next(100); 
+0

ありがとうございます。そのトリックをした。 – Afonso

+0

@Alfonso、@dlev:この例は[スレッドセーフではありません](http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx)です。 'Next'メソッドの呼び出しのたびにロックするべきです。そのため、独自のロックを行う独自の静的なRandomクラスを作成する方が簡単です。 – Groo

+0

@Grooは、私は承知しているが、それはそれはスレッドセーフではないことは無関係だ:OPは(単一スレッドのみを使用している最初のコードスニペットは、forループからそれらすべてを実行し、現在のスニペットは、ボタンのクリックに基づいてそれらを実行し、そのため、すべてUIスレッドから実行されます)。もちろん、一般的に正しいです。この問題について言及するためにアップデートを追加します。 – dlev

0

あなたはおそらく連続してすばやくオブジェクトのすべてのRunメソッドを呼び出しているので、それぞれのインスタンスRandomクラスは、with the same seedをインスタンス化し、同じ擬似乱数を返します。

この問題を解決するには、単一の(シングルトン)Randomインスタンスを持つスタティックランダムクラスを作成し、各呼び出し元が次の番号を連続して取得するようにします。

スレッドの安全性のためのいくつかのロックを使用すると、それはのようなものになります。

public class StaticRandom 
{ 
     private static readonly Random _r = new Random(); 
     private static object _lock = new object(); 

     public static int Next(int max) 
     { 
      lock (_lock) 
       return _r.Next(max); 
     } 
} 

をし、それをインスタンス化せずにそれを使用する:

// no need for the "Randomizer" field anymore 
int distance = StaticRandom.Next(100); 

ジョンスキートは、彼のmiscutilライブラリ内の完全な実装を持っていますいくつかと一緒にusage info