Как правильно использовать случайный класс в многопоточном приложении - PullRequest
4 голосов
/ 26 октября 2011

Мне нужно использовать случайный класс для генерации случайных чисел в многопоточном приложении внутри публичной статической функции.Как я могу этого достичь.В настоящее время функция ниже работает очень хорошо, но она не очень быстрая по сравнению со случайным классом.Поэтому мне нужно изменить приведенную ниже функцию и заставить ее работать со случайным классом, пока с этим классом происходят тысячи одновременных вызовов.если я использую random, он использует одно и то же начальное число для каждого вызова, и рандомизация оказывается очень плохой.мой текущий класс

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;

public static class GenerateRandomValue
{
    static RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();

    public static int GenerateRandomValueDefault(int irRandValRange)//default min val 1
    {
        if (irRandValRange == 0)
            irRandValRange = 1;
        byte[] randomNumber = new byte[4]; // 4 bytes per Int32
        Gen.GetBytes(randomNumber);
        return Math.Abs(BitConverter.ToInt32(randomNumber, 0) % irRandValRange) + 1;
    }

    public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
    {
        byte[] randomNumber = new byte[4]; // 4 bytes per Int32
        Gen.GetBytes(randomNumber);
        return BitConverter.ToInt32(randomNumber, 0) % irRandValRange + irMinValue;
    }
}

Еще одна функция, которая кажется довольно хорошей и безопасной для потоков

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;

public static class GenerateRandomValue
{
    private static Random seedGenerator = new Random();

    private static ThreadLocal<Random> random = new ThreadLocal<Random>(SeededRandomFactory);

    private static Random SeededRandomFactory()
    {
        lock(seedGenerator)
            return new Random(seedGenerator.Next());
    }

    public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
    {
        return random.Value.Next(irMinValue, irRandValRange);
    }
}

Ответы [ 4 ]

3 голосов
/ 27 октября 2011

Что вам нужно, так это лучший способ начать заполнение в вашем приложении ASP.NET, качество Random должно быть в порядке, используя метод ниже.

public static int GenerateRandomValueDefault(int irRandValRange)//default min val 1
{
    return GenerateRandomValueMin(irRandValRange, 1);
}

public static int GenerateRandomValueMin(int irRandValRange, int irMinValue)
{
    Random rand = GetRandom();
    return rand.GetNext(irMinValue,irRandValRange)
}

//This is a global random number generator, it is only used to provide the seed for the local RNG's.
private static Random GlobalRandom = new Random();

private static Random GetRandom()
{
    if (HttpContext.Current.Session["RNG"] == null)
    {
        //This lock is only hit the very first time the users Session state is used, every time after that it should use the cached local copy without taking the lock.
        lock(GlobalRandom)
        {
            //We use the Global RNG for seed instead of the default because it uses time to seed by default, and if two people get a new Random() within the same time-slice they will have the same seed. This prevents that from happening.
            HttpContext.Current.Session["RNG"] = new Random(GlobalRandom.Next());
        }
    }
    //Return the cached/new RNG.
    return (Random)HttpContext.Current.Session["RNG"];
}

У вас есть один экземпляр глобальной RNG, который блокируется, однако он срабатывает только при создании нового состояния сеанса, после чего сеанс использует только свою локальную копию. Вы получите очень хорошую производительность во время выполнения с небольшой нагрузкой при загрузке первой страницы на человека, поскольку она генерирует одно число из глобального хранилища.

Вы можете изменить это в соответствии со своими потребностями, но это даст вам общее представление, но даст общее представление.


Согласно предложению Хенка Холтермана, вот решение без блокировки, которое может быть быстрее и не использует HttpState.

private static int SeedCounter = 0;
private readonly object SeedInitLock = new Object();

private static Random GetRandom()
{
    //Do init the first time this function is ever called.
    if(SeedCounter == -1)
    {
        //The first time the function is called everyone will try to update SeedCounter, but only the first 
        //thread to complete it will be the value everyone uses.
        Random initRNG = new Random();
        Interlocked.CompareExchange(ref SeedCounter, initRNG.Next(), -1);

    }
    else if (SeedCounter < 0)
    {
        //Because Interlocked.Increment wraps the value to int.MinValue and Random(int) will take the absolute
        //value of the seed, we skip all of the negitive numbers and go to 0.
        Interlocked.CompareExchange(ref SeedCounter, 0, int.MinValue);
    }

    int tempSeed = Interlocked.Increment(ref SeedCounter);
    if (tempSeed < 0)
    {
        //If tempSeed is negative we hit a edge case where SeedCounter wrapped around. We just call the function
        //again so we do not reuse a seed that was just used.
        return GetRandom();
    }

    return new Random(tempSeed);
}
3 голосов
/ 26 октября 2011

Вы не указали никаких ограничений, кроме скорости, поэтому я думаю, что Random должно подойти.

// Field in the class
Random rand = new Random();

// Inside a method:
int randomValue = rand.Next(); // Random positive integer returned
int randomValue = rand.Next(max); // Random under max
int randomValue = rand.Next(min, max); // Random in range
0 голосов
/ 26 октября 2011

Предполагается, что использование Random с хорошим начальным числом лучше для повышения производительности Но вам нужна производительность?

Проверьте этот блог на метрики: http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx В конце есть подсказка о посеве у поставщика.

Взять хотя бы крупицу соли. Вы должны будете повторить тесты, чтобы быть уверенным.

0 голосов
/ 26 октября 2011

Я бы предложил System.Web.Security.Membership.GeneratePassword() метод.

string generated = System.Web.Security.Membership.GeneratePassword(
                   10, // maximum length
                   3)  // number of non-ASCII characters.
...