2013-03-04 2 views
3

Я работаю над проектом C#, который требует случайных чисел в цикле parallel.for. Для этого я использую класс StaticRandom из MiscUtil от Jon Skeet.C# Seeding StaticRandom in parallel.for для получения воспроизводимой последовательности

Для тестирования я хочу иметь возможность воспроизводить мои результаты. Поэтому я попытался выровнять базовую System.Random, чтобы получить одну и ту же последовательность при каждом запуске теста. Однако даже с семенем я получаю разные результаты каждый раз. В регулярном цикле цикла каждый раз выводится одна и та же последовательность. Существует код для воспроизведения моей проблемы ниже (вам нужно будет обновить ожидаемую последовательность с помощью вывода на вашем компьютере).

Есть ли способ семени Random, чтобы я мог получить воспроизводимую последовательность в цикле parallel.for?

[TestMethod] 
    public void MultiThreadedSeededRandom() 
    { 
     var actual = new ConcurrentBag<int>(); 
     Parallel.For(0, 10, i => 
     { 
      actual.Add(StaticRandom.Next(1000)); 
     }); 

     WriteActualToOutput(actual); 

     var expected = new int[] { 743, 51, 847, 682, 368, 959, 245, 849, 192, 440, }; 

     Assert.IsTrue(AreEqual(expected, actual.ToArray())); 
    } 

    public static bool AreEqual<T>(T[] expected, T[] actual) 
    { 
     if (expected.Length != actual.Length) 
      return false; 

     for (int i = 0; i < expected.Length; i++) 
     { 
      if (!expected[i].Equals(actual[i])) 
       return false; 
     } 
     return true; 
    } 

    private static void WriteActualToOutput(ConcurrentBag<int> acual) 
    { 
     var result = string.Empty; 
     result += "new int[] {"; 
     foreach (var value in acual) 
     { 
      result += value.ToString() + ","; 
     } 
     result += "};"; 

     Trace.WriteLine(result); 
    } 

    public static class StaticRandom 
    { 
     private static Random random = new Random(1231241); 
     private static object myLock = new object(); 

     public static int Next(int max) 
     { 
      object obj; 
      Monitor.Enter(obj = StaticRandom.myLock); 
      int result; 
      try 
      { 
       result = StaticRandom.random.Next(max); 
      } 
      finally 
      { 
       Monitor.Exit(obj); 
      } 
      return result; 
     } 
    } 
+0

Что делать, если вы приняли все случайные выборки во время запуска приложения и хранить их для последующего использования , Тогда неважно, в каком порядке использовались потоки выборки. –

+0

Кроме того, может быть, вся выборка сама по себе в конкретном потоке, не вызывающем Invoke? –

ответ

8

При использовании Parallel.For, дизайн, вы собираетесь получить результаты из последовательности, поскольку каждая итерация будет работать параллельно в недетерминистическую образом. Если вам нужна одна и та же последовательность «случайных» номеров, вам необходимо позвонить в Random.Next() в надежном порядке. Это не будет работать из нескольких потоков.

Вместо того, чтобы использовать Parallel.For для заполнения случайных чисел, вы должны заранее создать последовательность «случайных» чисел, затем используйте Parallel.For, чтобы выполнить обработку на основе этих чисел после факта. Таким образом, вы всегда будете генерировать числа в правильном порядке, что сохранит последовательность.

+0

Ах, да. Я предположил, что они были совершенно разными последовательностями, но на самом деле это только порядок, который отличается. Я попытаюсь сгенерировать случайные числа перед фактической параллелью. Благодаря! – mda

0

На отдельную ноте вы могли бы использовать эффективности StringBuilder в методе вывода, если вы никогда не использовали его:

private static void WriteToArrayString(ICollection items) 
    { 
     var result = new StringBuilder("new []{"); 

     var index = 0; 
     foreach(var value in items) 
     { 
      if (index == (items.Count - 1)) 
      { 
       result.Append(string.Concat(value)); 
       index++; 
       continue; 
      } 
      result.Append(string.Concat(value, ',')); 
      index++; 
     } 
     result.Append("};"); 

     Trace.WriteLine(result.ToString()); 
    }