locked
Guid.NewGuid erzeugt Duplikate RRS feed

  • Frage

  • OS: WinCE 6.0 auf einem Toradex Board mit PXA320.

    In meiner C#/.net-Anwendung ist es vorgekommen, dass Guids auf einem Gerät wiederholt vorgekommen sind. Das Mini-Programm weiter unten führt nach 10-20 Neustarts zu einer Exception, weil eine bereits in der Data-Tabelle vorkommende Guid ein zweites Mal erzeugt wird (DeviceHelper ist eine kleine Utility-Klasse, die den Neustart durchführt). Wie kann das sein? Was wird als Seed für Guid.NewGuid() verwendet? Kann ich den Seed irgendwie modifizieren?

     

     class Program
     {
     static void Main(string[] args)
     {
      using(var sql = new SqlCeDataProvider(Path.Combine(Helper.GetApplicationPath(), "guid_db.sdf"), string.Empty, string.Empty))
      {
       sql.ExecuteNonQuery(string.Format("INSERT INTO [Data] (Id) VALUES ('{0}');", Guid.NewGuid()));
      }
      Console.WriteLine("Sleep");
      Thread.Sleep(10000);
      DeviceHelper.Reboot(RebootMode.ColdReboot);
     }
     }
    
    
    
    

     



    Mittwoch, 17. August 2011 15:14

Antworten

  • Eine vermutete Ursache für das Problem ist, dass wir eine externe Real Time Clock haben. Wenn nun die Uhrzeit von der externen RTC erst übernommen wird, nachdem der Zufallszahlengenerator der Guid-Erzeugung initialisiert wird, kanns sein dass der Zufallszahlengenerator öfters mit dem selben Seed initialisiert wird.

    Unser Workaround ist nun, dass wir eine Random()-Instanz erzeugen, damit Zufalls-Byte-Sequenzen erzeugen und die für die Guid-Erzeugung verwenden:

     

      public static class GuidHelper
      {
        private static Random sRandom;
    
        public static Guid NewGuid()
        {
          if (sRandom == null)
          {
            var dateTicks = DateTime.Now.Ticks;
            var environmentTicks = Environment.TickCount;
            var seed = (int)(((dateTicks / 1000 + environmentTicks) % uint.MaxValue) - int.MaxValue - 1);
            sRandom = new Random(seed);
          }
    
          var rawGuid = new byte[16];
          sRandom.NextBytes(rawGuid);
          return new Guid(rawGuid);
        }
      }
    


    • Als Antwort markiert michivo Dienstag, 30. August 2011 13:00
    Dienstag, 30. August 2011 13:00

Alle Antworten

  • Hi,

    rein theoretisch kann das zwar passieren, ich glaube aber nicht, dass das Problem wirklich bei NewGuid zu suchen ist. Kann es sein, dass dein ID Feld zu kurz ist und nicht der ganze String reinpasst?

    Ich nehme an, dass Du die Methode jeweils wieder nach dem Reboot aufrufst, nicht vor dem Reboot 10, 20 mal, oder?

     


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
    Mittwoch, 17. August 2011 15:36
  • Hi,

    erstmal vielen Dank für die Antwort. Das ID Feld ist vom Typ uniqueidentifier, hat also die vollen 16 bytes. Wenn die Id-Spalte kein PK ist bzw. ich keinen UNIQUE-constraint drauf habe, sehen wir tatsächlich doppelte Guids in der Tabelle.

    Die Methode wird, wie Du richtig annimmst, nach jedem Reboot genau einmal aufgerufen (Programm liegt im Startup-Folder), danach wird neu gestartet.

    In der Echt-Applikation wo wir das Problem erstmals beobachtet haben ists auch so, dass wir eigentlich nur dann doppelte Guids bekommen, wenn unser Programm automatisiert bedient wird (d.h. es gibt keine direkte User-Interaktion in Form von Keyboard- oder Mouse-Inputs, das Gerät wird über die serielle Schnittstelle gesteuert).

    • Als Antwort markiert michivo Dienstag, 30. August 2011 13:00
    • Tag als Antwort aufgehoben michivo Dienstag, 30. August 2011 13:00
    Donnerstag, 18. August 2011 06:53
  • Eine vermutete Ursache für das Problem ist, dass wir eine externe Real Time Clock haben. Wenn nun die Uhrzeit von der externen RTC erst übernommen wird, nachdem der Zufallszahlengenerator der Guid-Erzeugung initialisiert wird, kanns sein dass der Zufallszahlengenerator öfters mit dem selben Seed initialisiert wird.

    Unser Workaround ist nun, dass wir eine Random()-Instanz erzeugen, damit Zufalls-Byte-Sequenzen erzeugen und die für die Guid-Erzeugung verwenden:

     

      public static class GuidHelper
      {
        private static Random sRandom;
    
        public static Guid NewGuid()
        {
          if (sRandom == null)
          {
            var dateTicks = DateTime.Now.Ticks;
            var environmentTicks = Environment.TickCount;
            var seed = (int)(((dateTicks / 1000 + environmentTicks) % uint.MaxValue) - int.MaxValue - 1);
            sRandom = new Random(seed);
          }
    
          var rawGuid = new byte[16];
          sRandom.NextBytes(rawGuid);
          return new Guid(rawGuid);
        }
      }
    


    • Als Antwort markiert michivo Dienstag, 30. August 2011 13:00
    Dienstag, 30. August 2011 13:00