Я только что начал играть с Библиотекой Параллели Задачи и столкнулся с интересными проблемами; у меня есть общее представление о том, что идет, но хотело бы услышать комментарии от людей, более компетентных, чем я помочь понять то, что происходит. Мои извинения за несколько длинный код.
Я запустил с непараллельного моделирования случайного блуждания:
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 слишком близко вовремя, приводя к идентичным последовательностям).
Любое понимание в том, что продолжается, было бы очень ценно мне!
Ни один из этих подходов не даст вам действительно хороших случайных чисел.
В этом блоге описано множество подходов для получения более качественных случайных чисел с помощью 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. Это безопасно для потоков.
Класс Random
не является потокобезопасным; если вы используете его в нескольких потоках, он может испортиться.
Вы должны создать отдельный экземпляр Random
для каждого потока и убедиться, что они не используют одно и то же начальное число. (например, Environment.TickCount * Thread.CurrentThread.ManagedThreadId
)