Как распараллелить цикл for с помощью PLINQ?

У меня есть задача распараллелить итерации с помощью PLINQ. Для этого у меня есть функция, основанная на цикле for:

public void PointGenerator(int n, Random rnd)
{
    for (int i = 1; i <= n; i++)
    {
        x = rnd.NextDouble(); 
        y = rnd.NextDouble(); // 
        if (((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5)) < 0.25)
        {
            N_0++;
        }
    }
}

Как это сделать с помощью PLINQ?


person pragmus    schedule 24.08.2014    source источник


Ответы (2)


Вы можете попробовать так:

public bool Check(double x, double y)
{
    return ((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5)) < 0.25;    
}

// ...

N_0 = Enumerable
    .Range(0, n)
    .Select(i => new { X = rnd.NextDouble(), Y = rnd.NextDouble() })
    .AsParallel()
    .Count(p => Check(p.X, p.Y));

Как отметил Роман Двоскин в комментарии под другим ответом, Random не являются потокобезопасными:

Однако объекты Random не являются потокобезопасными. Если ваше приложение вызывает методы Random из нескольких потоков, вы должны использовать объект синхронизации, чтобы гарантировать, что только один поток может получить доступ к генератору случайных чисел за раз. Если вы не гарантируете, что доступ к объекту Random осуществляется потокобезопасным способом, вызовы методов, возвращающих случайные числа, возвращают 0.

Эту проблему можно обойти, создав сначала все пары XY.

person BartoszKP    schedule 24.08.2014

Используйте Parallel.ForEach в диапазоне :

var randLock = new object();
Parallel.ForEach(
    Enumerable.Range(1, n)
,   () => { // if you needed the index, you could use i instead of ()
        lock (randLock) {
            x = rnd.NextDouble(); 
            y = rnd.NextDouble();
        }
        if (((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5)) < 0.25) {
            Interlocked.Increment(ref N_0);
        }
    });

Обратите внимание на использование Interlocked.Increment вместо N_0++ чтобы избежать проблем с параллелизмом. Использование randLock служит той же цели: согласно документации, System.Random не является потокобезопасным.

person Sergey Kalinichenko    schedule 24.08.2014
comment
Random не является потокобезопасным согласно MSDN msdn .microsoft.com/en-us/library/System.Random(v=vs.110).aspx - person Roman Dvoskin; 24.08.2014
comment
Какова точка параллели при использовании замка? Random не поддается распараллеливанию, и никакая другая работа не выполняется. @BartoszKP имеет более правильный ответ. Хотя ни то, ни другое не является полностью правильным, поскольку в Random используется несколько потоков. - person Roman Dvoskin; 24.08.2014
comment
@RomanDvoskin Часть за пределами lock не бесплатна, но я согласен, вызов rnd.NextDouble(), вероятно, будет доминировать. - person Sergey Kalinichenko; 24.08.2014
comment
Бесплатен только код, который не выполняется. Замки, нитки имеют большую стоимость. Нефункциональный стиль с побочными эффектами имеет самый большой эффект. - person Roman Dvoskin; 24.08.2014