如何在多线程应用程序中正确使用随机类

发布于 2024-12-11 23:43:19 字数 1723 浏览 0 评论 0原文

我需要使用随机类在公共静态函数内的多线程应用程序中生成随机数。我怎样才能实现它。目前,下面的函数运行得很好,但与随机类相比,它不是很快。因此,我需要修改下面的函数并使其与随机类一起使用,同时该类正在发生数千个并发调用。如果我使用随机,它会为我认为的每个调用使用相同的种子,并且随机化非常糟糕。 我当前的类

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);
    }
}

I need to use random class to generate random numbers in a multi threaded application inside public static function. How can i achieve it. Currently the function below is working very well but it is not very fast when compared to random class. So i need to modify the function below and make it work with random class while thousands of concurrent calls are happening to that class. if i use random it uses same seed for every call i suppose and the randomization is being very bad.
my current class

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;
    }
}

Another function which seems pretty good and thread safe

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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

悍妇囚夫 2024-12-18 23:43:19

您需要的是一种更好的方法来在 ASP.NET 应用程序中开始播种,使用以下方法,随机的质量应该很好。

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 实例,但是只有在生成新的会话状态时才会发生这种情况,之后会话仅使用其本地副本。您将在运行时获得非常好的性能,每人第一页加载的负载很小,因为它从全局存储中生成一个数字。

您可以修改它以满足您的需要,但它为您提供了总体思路,但它为您提供了总体思路。


根据 Henk Holterman 的建议,这里有一个无锁解决方案,可能更快并且不使用 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);
}

What you need is a better way to start the seeding in your ASP.NET application, the quality of Random should be fine using the below method.

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"];
}

You have one instance of a global RNG that does lock, however this is only hit when a new session state is generated, after that the session uses only it's local copy. You will get very good performance at run time with a slight load on the first page load per person as it generates one number from the global store.

You can modify this to suit your needs but it gives you the general idea, but it gives you the general idea.


Per Henk Holterman's suggestion, here is a lock less solution that may be faster and does not use 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);
}
请帮我爱他 2024-12-18 23:43:19

除了速度之外,您没有指定任何限制,所以我认为 < code>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

You didn't specify any constraints other than speed, so I would think that Random should do.

// 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
嘴硬脾气大 2024-12-18 23:43:19

我建议使用 System.Web.Security.Membership.GeneratePassword() 方法。

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

I would suggest System.Web.Security.Membership.GeneratePassword() method.

string generated = System.Web.Security.Membership.GeneratePassword(
                   10, // maximum length
                   3)  // number of non-ASCII characters.
舟遥客 2024-12-18 23:43:19

使用具有良好种子的随机应该会更好地“性能明智”。但你需要性能吗?

检查此博客的指标:http://blogs.msdn .com/b/pfxteam/archive/2009/02/19/9434171.aspx
最后有一个关于与提供商播种的提示。

不过,请持保留态度。您必须重做测试才能确定。

Using Random with a good seed is suppose to be "performance wise" better. But do you need the performance ?

Check this blog for metrics : http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx
There is a hint at the end about seeding with the provider.

Take it with a grain of salt though. You will have to redo the tests to be sure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文