【问题标题】:Why does this method spit out the same results to different users为什么这种方法向不同的用户吐出相同的结果
【发布时间】:2011-09-07 16:18:33
【问题描述】:

所以我有生成条形码和带有该条形码标签的方法。我们注意到,当多个用户同时生成条形码时,他们都会得到相同的文件。我正在使用 ASP.NET,并且在内部服务器上托管应用程序和文件。

public void trickylabel(string fnsku, string title)
{
    Random random = new Random();
    int randomNumber = random.Next(0, 100000);

    //Set barcode properties...

    code.parse(fnsku); // Text

    BCGDrawing drawing = new BCGDrawing(this.Server.MapPath("~") + "image"+ randomNumber.ToString() +".png", color_white);
    drawing.setBarcode(code);
    drawing.draw();

    // Draw (or save) the image into PNG format.
    Response.ContentType = "image/png";
    drawing.finish(ImageFormat.Png);

    Document doc = new Document(new iTextSharp.text.Rectangle(200f, 75f), 20F, 10F, 10F, 1F);
    PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(Request.PhysicalApplicationPath +
        "\\"+randomNumber.ToString()+".pdf", FileMode.Create));

    doc.Open();

    iTextSharp.text.Image png = iTextSharp.text.Image.GetInstance(this.Server.MapPath("~") + "image" + randomNumber.ToString() +".png");

    doc.Add(png);

    //Sets pdf properties...

    doc.Add(new Paragraph(title, times));
    PdfAction action = new PdfAction(PdfAction.PRINTDIALOG);
    writer.SetOpenAction(action);
    doc.Close();

    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-Disposition", "attachment; filename=labels.pdf");
    Response.TransmitFile(Server.MapPath("~/"+randomNumber.ToString()+".pdf"));

}

【问题讨论】:

    标签: c# asp.net random itextsharp


    【解决方案1】:

    问题出在这里:

    Random random = new Random();
    int randomNumber = random.Next(0, 100000);
    

    Random 类生成伪随机序列,它基于一些起始值,称为 Seed。如果两个 Random 实例使用相同的种子进行初始化,它们将产生相同数量的序列。

    当使用无参数构造函数创建随机时,种子是从当前系统时间创建的。如果您运行一个创建 Random 类实例的循环,您会注意到 random.Next() 值每秒仅更改几次(每 16 毫秒一次)。

    为避免这种情况,您应该重用初始化的 Random 实例(但是,由于您使用的是 ASP.Net,您需要确保 random.Next() 的调用在线程安全中执行方法)。然后,您的访问者将在每次新的 random.Next(...) 调用时获得不同的值。

    或者 Random 有一个构造函数,允许手动设置初始种子值。因此,您可以创建自己的算法来为您的用户创建独特的种子。

    更新 线程安全的随机化器实现。 在程序集加载时初始化。

    用法:只需将random.Next(0, 100000);替换为Randomizer.Next(0, 100000);

    public static class Randomizer
    {
        private static Random rnd;
        static Randomizer()
        {
            rnd = new Random();
            rndlock = new object();
        }
    
        private static object rndlock;
        public static int Next(int minValue, int maxValue)
        {
            lock(rndlock)
            {
                return rnd.Next(minValue, maxValue);
            }
        }
    }
    

    更新关于线程安全和不可预知的结果

    如上所述,Random 生成伪随机序列。这意味着该序列中的每个 int 数字都知道前一个和下一个数字。这意味着在一定数量的呼叫后,这些数字将开始重复。 Random.Next() 算法旨在最大化此序列中唯一项目的数量。

    那么,在这种情况下,“不可预测”是什么意思?当多个线程同时使用相同的变量时,算法的逻辑就会被破坏。在最好的情况下,这会使相同数字的序列更频繁地出现。在最坏的情况下 Random.Next() starts to produce zero values on each call 没有任何恢复的机会。

    有关 System.Random 的更多有用信息,请访问MSDN

    【讨论】:

    • 仅供参考... Random() 使用 Environment.TickCount 进行初始化
    • @Paul Walls:谢谢!我想知道为什么 DateTime.Now.Ticks 的变化比 Random 的种子更频繁。在我的机器上,这个值每 16 毫秒只改变一次,所以你不能相信两个连续的访问者会有不同的 Datetime.Now.Ticks。 16 毫秒是获得重复结果的巨大时间。
    • @Artemix:关于 "...例如,您可以将 Random 实例存储在某个静态成员中"不要那样做! Random 不是线程安全的;如果您将单个实例存储在静态中,那么除非您自己强制执行某种同步,否则您将获得更奇怪(即损坏)的结果。
    • @Artemix 我一次运行的线程不超过一个。我需要担心这个吗?
    • @Joe Tyman:是的,你需要担心。在 ASP.Net 中,程序集被加载一次,并被服务器进程的所有请求重用。因此静态成员对所有用户都是通用的(只有在服务器重新启动时才会重新初始化)。每个请求都由单独的线程处理,因此如果多个用户同时请求页面,则多个线程可能会同时引用静态成员。
    【解决方案2】:

    或者直接使用NewGuid Method

    【讨论】:

    • 我同意,100,000 似乎对于“唯一性”来说是很大的空间,但与 GUID 的空间相比,它微不足道
    • @Chris Haas:但是在这种情况下,将 GUID 限制为条形码 ID 可能会比普通的 Random.Next(0, 100000) 使用产生更多不希望的匹配。此外,如果您正在寻找替代随机数源,您可能对使用 RNGCryptoServiceProvider 类感兴趣:msdn.microsoft.com/en-us/library/…
    • @Artemix,我同意 GUID 永远不应该被修剪或只被截取一部分。由于要求是创建独特的东西,并且 GUID 只需要 32 个字符(如果您使用像 ShortGuid 这样的字符则更少),这似乎是一个合乎逻辑的选择。随机性和独特性是非常不同的,这个项目对我来说没有任何“随机性”。
    • @Chris Haas:根据本文档ietf.org/rfc/rfc4122.txt C# 生成的 GUID 是随机类型,具有以下模式:xxxxxxxx-xxxx-4xxx-Vxxx-xxxxxxxxxxxx,其中 x 是随机数,V 是位布局为 10yy 的数字,其中 yy 是两个随机位。所以,GUID 实际上有 122 个随机位。如您所见,您在这里没有任何唯一性优势(随机位数除外),您可以改用 RNGCryptoServiceProvider。是的,您可以在 C# 中使用 GUID 作为便宜的随机发生器(就代码而言)。除非它将在未来版本的框架中更改。
    【解决方案3】:

    如果请求同时开始,那么产生的随机数很可能彼此相同。在不提供种子值的情况下,Random 构造函数根据系统时钟获取其种子,因此两个实例可以生成相同的一组随机值(它们不是随机的,它们是伪随机的)。

    此处的文档 - http://msdn.microsoft.com/en-us/library/system.random.aspx

    尝试使您的 Random 实例成为该类的静态成员,并让您的方法只在该成员上调用 .Next。

    【讨论】:

      【解决方案4】:

      您需要将 pdf 保存到磁盘吗?

      我建议将 png 图像和 pdf 文档保存到 MemoryStream。 这也可能会带来更好的性能,因为磁盘访问更少。

      public void trickylabel(string fnsku, string title)
      {
          //Set barcode properties...
      
          code.parse(fnsku); // Text
      
          BCGDrawing drawing = new BCGDrawing(color_white);
          drawing.setBarcode(code);
          drawing.draw();
      
          // Draw (or save) the image into PNG format.
      
          using (var imageStream = new MemoryStream())
          {
              drawing.finish(ImageFormat.Png, imageStream);
      
              using (var result = new MemoryStream())
              {
                  Document doc = new Document(new iTextSharp.text.Rectangle(200f, 75f), 20F, 10F, 10F, 1F);
                  PdfWriter writer = PdfWriter.GetInstance(doc, result);
      
                  doc.Open();
      
                  var png = iTextSharp.text.Image.GetInstance(System.Drawing.Image.FromStream(imageStream), ImageFormat.Png);
                  doc.Add(png);
      
                  //Sets pdf properties...
      
                  doc.Add(new Paragraph(title, times));
                  PdfAction action = new PdfAction(PdfAction.PRINTDIALOG);
                  writer.SetOpenAction(action);
                  doc.Close();
      
                  Response.ContentType = "application/pdf";
                  Response.AppendHeader("Content-Disposition", "attachment; filename=labels.pdf");
                  Response.BinaryWrite(result.ToArray());
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-04-19
        • 1970-01-01
        • 1970-01-01
        • 2019-12-16
        • 1970-01-01
        • 2016-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多