Guid.NewGuid()返回重复值?

10
我们有一个应用程序用于生成模拟数据以测试我们的服务。每个数据项都有一个唯一的Guid。但是当我们在模拟器中进行了一些小的代码更改后运行测试时,它生成的所有对象都具有相同的Guid。
首先创建一个数据对象,然后在for循环中修改对象的属性(包括新的唯一Guid)并通过远程调用 (序列化的方式,而不是引用传递) 将其发送到服务端。之后继续循环并重复上述过程。
如果我们在循环内部加入短暂的Thread.Sleep(...)语句,它就会生成唯一的id。但我认为这只是一个红鲱鱼。我创建了一个测试应用程序来连续创建Guid,没有出现重复。
我的理论是IL以某种方式进行了优化,导致了这种行为。但是足够谈论我的理论了,您认为呢?我对建议和测试方法持开放态度。
更新:有很多人对我的问题感到困惑,所以让我澄清一下。我不认为NewGuid()有问题。显然它是有效的。它没问题!但是,这里有一个Bug,导致NewGuid()要么: 1. 在我的循环中只被调用一次 2. 在我的循环中每次都被调用,但只被分配一次 3. 我还没有想到的其他情况
这个Bug可能在我的代码中(最有可能的)或者是优化中。
所以,再次重申我的问题:我应该如何调试这种情况?
(谢谢大家的讨论,这确实帮助我在脑海中澄清了问题)
更新2:我很想发布一个显示问题的示例,但这也是我的问题之一。我无法在整个应用程序套件(客户端和服务器)之外复制它。
以下是相关片段:
OrderTicket ticket = new OrderTicket(... );

for( int i = 0; i < _numOrders; i++ )
{
    ticket.CacheId = Guid.NewGuid();
    Submit( ticket );  // note that this simply makes a remoting call
}

如果你认为IL中存在一个bug,请使用Reflector来跟踪它。 - Jim Burger
啊,是的。我想到了。但我的初步猜测是它可能会在JIT优化器中。不能用Reflector来查看。 - dviljoen
如果您认为这不是NewGuid的错误,请发布一些显示问题的代码。或者,至少发布曾经出现问题的代码。此外,请回答是否删除Thread.Sleep会导致问题重新出现的问题。我在心理调试方面相当擅长 - 但有了一些细节更好。 - Mark Brackett
是的。线程休眠使其正确运行,而删除它则会返回错误。让问题更加复杂的是,我完全无法在整个应用程序套件之外重现这个问题。 - dviljoen
7个回答

21

Submit方法是异步调用吗?或者在任何阶段ticket对象都进入另一个线程吗?

在代码示例中,您正在重用同一对象。如果Submit在短暂延迟后在后台线程中发送票据(并且不复制),那么更改CacheId实际上会更新所有挂起的提交。这也解释了为什么Thread.Sleep可以解决该问题。请尝试此操作:

for( int i = 0; i < _numOrders; i++ )
{
    OrderTicket ticket = new OrderTicket(... );
    ticket.CacheId = Guid.NewGuid();
    Submit( ticket );  // note that this simply makes a remoting call
}

如果由于某种原因无法进行此操作,请尝试以下方法,看看它们是否仍然相同:

ticket.CacheId = new Guid("00000000-0000-0000-0000-" + 
     string.Format("{0:000000000000}", i));

4
看起来我终究赢了那个赌注。 :) - MusiGenesis

7

成千上万的开发者在.NET中使用Guid。如果Guid.NewGuid()有任何倾向于固定一个值,那么这个问题早就已经遇到了。

这里肯定是代码的小改变造成了问题。 Thread.Sleep(不仅是一个红鲱鱼,而且是在太阳下腐烂的鱼)“解决”了你的问题,这表明你的属性被以一种奇怪的方式设置,直到循环停止阻塞(通过结束或者Thread.Sleep),才能生效。我甚至愿意打赌,“小改变”是从一个单独的线程重置所有属性。

如果您发布一些示例代码,那将会有所帮助。


1
你会输掉这个赌注。这是一个单线程,仅通过网络生成对象数据。 - dviljoen
好人。我打算随机给你的答案点赞一堆。 :) 对于“鱼在阳光下腐烂”的评论感到抱歉。 - MusiGenesis

3

你的代码有一个bug。如果你已经生成了多个guid,那么这很可能是最合理的解释。线索在你的问题中: “当我们对模拟器进行了一些小的代码更改后运行测试,所有由其生成的对象都具有相同的Guid”。


是的,我也是这么想的。但是我找不到它。每次迭代都会调用NewGuid(),而每次返回的都是相同的ID。有什么建议吗? - dviljoen
这就是随机数生成器的问题,你永远无法确定。 - tsilb
我不相信这个。GUID中有一些随机性,理论上重复是“可能的”,但并非每一个都会重复。这显然不是问题所在。我们的代码或优化器肯定出了问题。我的问题是我不知道如何找出是哪一个出了问题。有什么建议吗? - dviljoen
把代码缩减到最小化,只保留能够重现问题的部分,并在此处发布。 - Mitch Wheat

2
请看这篇关于如何创建Guid的文章
本文源自于这个答案。
总之,如果你创建GUID太快而时钟没有向前移动,那么你会得到一些相同的结果。然而,当你加入一个延迟时,它会起作用,因为时钟已经移动了。

谢谢,但这并没有帮助我。我知道Guid在统计上是唯一的。我的问题不是我有两个重复的Guid中的10,000个Guid之一。我的问题是10,000个Guid中的10,000个都是重复的。这不是统计碰撞。这是一个错误。但是在哪里?如何找到? - dviljoen
抱歉,我不得不给你投反对票,因为你没有完全阅读你提供的文章。 - MusiGenesis

2
在Submit和OrderTicket中的代码也会有所帮助...
你正在重复使用OrderTicket。我怀疑您(或远程本身)正在批处理调用 - 可能与连接数/主机限制有关 - 并在最终发送它们时选择CacheId的最后一个值。
如果您对应用程序进行调试或Thread.Sleep,您将更改时间,以便远程调用在您分配新的CacheId之前完成。
您是否异步调用远程调用?我认为同步调用会阻塞 - 但我会使用诸如Wireshark之类的数据包嗅探器进行检查,以确保。无论如何,只需更改为在每次迭代中创建一个新的OrderTicket即可解决问题。
编辑:问题不是NewGuid被破坏了...因此,我已删除先前的答案。

我知道这是一个bug。但就像这里其他几个人一样,你似乎过于关注Guid的唯一性。这不是问题所在。再说一遍,10000个中有2个重复GUID将是一个唯一性问题。但我现在得到了10000个完全相同的Guid。 - dviljoen
如果你知道这是一个 bug(意思是你认为这是应用程序的 bug),那么为什么标题是“Guid.NewGuid() 返回重复项”,而且还有一个理论认为这是 JIT 优化器引起的问题呢? - Mark Brackett
关于“Guid的唯一性”,我提出的观点根本不是固定在这里。熵值耗尽或未捕获的失败分配(例如)可能会导致各种各样的问题,包括重复的Guid(潜在地 - 再次强调,这是针对您的“选择被破坏”的想法而言的牵强理论)。 - Mark Brackett
请阅读我上面的更新。我并不是说NewGuid有问题。我从来没有打算暗示这一点(回想起来,我只是在读我的帖子标题,是的,它听起来像...抱歉)。我也没有“select有问题”的心态。我只是有一个我不知道如何找到的错误。 - dviljoen

1

我还不知道GUID是如何生成的细节。但是目前我的组织正在以比兔子更快的速度繁殖GUID。因此,我可以证明GUID尚未被破解。

  • 如果可能的话,请发布源代码或克隆可重现应用程序。很多时候,创建克隆应用程序以重现问题的行为会向我展示问题所在。
  • 另一种方法是注释掉“那些小改动”。如果这样修复了问题,您就可以三角化以找到有问题的代码行。仔细检查这些小改动……我是说真的非常仔细。

请告诉我们进展如何……这听起来很有趣。


如果 Guid.NewGuid() 每几百万次调用就会产生重复,甚至每一次都会产生重复,那么我在专业领域会遇到大麻烦。 - MusiGenesis
今天我遇到了一个随机的问题。GUID.NewGUID()返回了完全相同的值。尽管应用程序已经运行了多年,但这只是随机发生的。我只是使用GUID来获取一个唯一的代码,所以对我来说,一个简单的解决方法就是将循环计数器附加到GUID上。 - Allen King

0

我的直觉告诉我这里可能发生了类似的事情...

class OrderTicket 
{
   Guid CacheId {set {_guid = new Guid("00000000-0000-0000-0000-");}
}

每次调用时,将CacheId的值和堆栈跟踪记录到日志文件中...也许是其他人设置了它。

不是。它只是包装了_cacheId,而_cacheId是初始化为Guid.NewGuid()的。 - dviljoen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接