Параллельные циклы и Случайный приводят к нечетным результатам

Я только что начал играть с Библиотекой Параллели Задачи и столкнулся с интересными проблемами; у меня есть общее представление о том, что идет, но хотело бы услышать комментарии от людей, более компетентных, чем я помочь понять то, что происходит. Мои извинения за несколько длинный код.

Я запустил с непараллельного моделирования случайного блуждания:

 var random = new Random();
 Stopwatch stopwatch = new Stopwatch();

 stopwatch.Start();

 var simulations = new List<int>();
 for (var run = 0; run < 20; run++)
 {
    var position = 0;
    for (var step = 0; step < 10000000; step++)
    {
       if (random.Next(0, 2) == 0)
       {
          position--;
       }
       else
       {
          position++;
       }
    }

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position));
    simulations.Add(position);
 }

 Console.WriteLine(string.Format("Average position: {0} .", simulations.Average()));
 stopwatch.Stop();

 Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds));
 Console.ReadLine();

Я затем записал свою первую попытку параллельного цикла:

 var localRandom = new Random();

 stopwatch.Reset();
 stopwatch.Start();

 var parallelSimulations = new List<int>();
 Parallel.For(0, 20, run =>
 {
    var position = 0;
    for (var step = 0; step < 10000000; step++)
    {
       if (localRandom.Next(0, 2) == 0)
       {
          position--;
       }
       else
       {
          position++;
       }
    }

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position));
    parallelSimulations.Add(position);
 });


 Console.WriteLine(string.Format("Average position: {0} .", parallelSimulations.Average()));
 stopwatch.Stop();

 Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds));

 Console.ReadLine();

Когда я выполнил его на наборе виртуальной машины для использования 1 ядра только, я наблюдал подобную продолжительность, но выполнения больше не обрабатываются в порядке - не удивительно.

Когда я выполнил его на двухъядерной машине, дела шли нечетные. Я не видел улучшения вовремя и наблюдал некоторые очень странные результаты для каждого выполнения. Большинство выполнений заканчивается с результатами-1 000 000, (или очень близко), который указывает что Случайный. Затем возвращает 0 квази все время.

Когда я делаю случайное локальное для каждого цикла, все работает просто великолепно, и я получаю ожидаемое улучшение продолжительности:

Parallel.For(0, 20, run =>
         {
            var localRandom = new Random();
            var position = 0; 

Мое предположение - то, что проблема имеет отношение к тому, что Случайный объект совместно используется циклами и имеет некоторое состояние. Отсутствие улучшения продолжительности в "провальной параллельной" версии, я принимаю из-за того того, что вызовы к Случайному не обрабатываются параллельно (даже при том, что я вижу, что параллельная версия использует оба ядра, тогда как оригинал не делает). Часть, которую я действительно не получаю, - то, почему результаты симуляции - каковы они.

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

Любое понимание в том, что продолжается, было бы очень ценно мне!

8
задан Mathias 27 May 2010 в 20:22
поделиться

2 ответа

Ни один из этих подходов не даст вам действительно хороших случайных чисел.

В этом блоге описано множество подходов для получения более качественных случайных чисел с помощью Random

http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx

Они могут подойти для многих повседневных приложений.

Однако если вы используете один и тот же генератор случайных чисел на нескольких потоках даже с разными семенами , вы все равно повлияете на качество ваших случайных чисел. Это происходит потому, что вы генерируете последовательности псевдослучайных чисел, которые могут накладываться друг на друга.

В этом видео объясняется более подробно:

http://software.intel.com/en-us/videos/tim-mattson-use-and-abuse-of-random-numbers/

Если вам нужны действительно случайные числа, то вам действительно нужно использовать криптографический генератор случайных чисел System.Security.Cryptography.RNGCryptoServiceProvider. Это безопасно для потоков.

2
ответ дан 5 December 2019 в 23:13
поделиться

Класс Random не является потокобезопасным; если вы используете его в нескольких потоках, он может испортиться.

Вы должны создать отдельный экземпляр Random для каждого потока и убедиться, что они не используют одно и то же начальное число. (например, Environment.TickCount * Thread.CurrentThread.ManagedThreadId )

2
ответ дан 5 December 2019 в 23:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: