【问题标题】:How to use random class in multi threaded application correct如何在多线程应用程序中正确使用随机类
【发布时间】:2011-12-16 00:17:22
【问题描述】:

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

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

【问题讨论】:

  • Random 类不够好有什么原因吗?
  • 没用?以什么方式?如果您只对速度感兴趣,那很好。您没有指定其他任何内容。请做。
  • 我建议阅读:random.org/randomness
  • @MontsterMMORPG:如果你解释一下“随机完美”的实际含义——即你的实际要求是什么——那么你可能会得到一些更中肯的答案。
  • @MonsterMMORPG 你应该编辑你的帖子并清楚简洁地解释你正在尝试做什么,你目前是如何做的,它到底是如何不工作的,以及你的要求是什么。

标签: c# asp.net random numbers


【解决方案1】:

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

您可以对其进行修改以满足您的需要,但它会为您提供总体思路,但也可以为您提供总体思路。


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

【讨论】:

  • 非常感谢您的出色回答。你认为你的功能和我当前的功能会有显着的性能差异吗?你的方式也是100%随机的吧?不受多个用户的影响
  • 为了提高性能,您只需要同时运行两者并查看结果,但我怀疑它会更快,但唯一确定的方法是测试。但是对于您的用例,即使他们都同时连接到服务器,这也会为每个用户生成完全不同的随机数。
  • +1 用于控制种子。但是将它存储在会话中似乎有点过分(并且是随机序列化的?)。每个请求一个实例可能会做。然后种子需要一些东西。
  • 一种可能的解决方案是每个线程只有一个 Random 实例。在 .NET4 或更高版本中,您可以使用静态 ThreadLocal&lt;Random&gt;;在旧版本中,您可以使用带有ThreadStatic 属性标记的静态Random
  • @LukeH 你将如何为那些 ThreadLocal 实例播种?如果您只使用默认构造函数,您将遇到与 OP 相同的问题,因为两个人使用相同的时间片作为种子。
【解决方案2】:

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

【讨论】:

  • 奥德是对的。 RNGCryptoServiceProvider 用于生成加密的强随机数。如果你没有这个要求,坚持 Random 类。
  • random 是此类的 1/10。我还需要保持随机完美。
  • @MonsterMMORPG - 为什么需要“随机完美”?
  • Oded,看看 OP 的手柄。如果这适用于任何类型的在线游戏(或投注),建议使用 RNG。
  • @HenkHolterman - 很公平,尽管他似乎不愿意解释他需要的随机函数所需的约束(除了速度)。
【解决方案3】:

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

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

【讨论】:

  • 但使用这种方法我无法确定随机数范围。我的意思是在 100 到 1000 之间我不正确吗?
  • 不,不是。顺便说一句,这有什么关系?
  • 是的,这很重要。我必须能够给出范围。大概在 200 到 250 之间。
  • 仅供参考,GeneratePassword() 内部使用 RNGCryptoServiceProvider -- 与 OP 相同。
【解决方案4】:

使用具有良好种子的 Random 被认为是“性能方面”更好。但是你需要性能吗?

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

不过,还是要加点盐。您必须重做测试才能确定。

【讨论】:

  • 与此相比,随机类根本不是随机的。您可以生成 1 到 1000 之间的随机数并生成 100 万个数字并进行比较。
  • 我发现了我的问题。我在我的 asp.net 应用程序中直接使用随机类。因此发生了数千个并发呼叫。 msdn的某人刚刚指出了这一点。那么在 asp.net 上使用随机类时,我应该如何修改上面的函数才能正常工作?我的意思是问题是种子。它正在生成所有相同的值。
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2014-01-14
  • 2015-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-19
  • 1970-01-01
相关资源
最近更新 更多