【问题标题】:Idiomatic "guaranteed-unique" identifiers in C++C++ 中惯用的“保证唯一”标识符
【发布时间】:2011-08-05 08:45:57
【问题描述】:

是否有一种惯用的 C++ 方法来保留和回收保证唯一的标识符?我的要求是:

  1. 假设存在当前未保留的 ID,reserve_id(void) 将返回该 ID。
  2. 在reserve_id() 调用的连续序列中,不会返回单个标识符两次
  3. 有一个函数 recycle(id_type) 将标识符返回给可用池。

例如,我见过 Boost::Uuid,但是 a) 我没有看到断言两个 UUID 保证唯一性的文档,并且 b) 我暂时受限于 Boost (1.40) 的早期版本.如果这特别适合这项任务,我可以推动升级。

【问题讨论】:

  • 您的要求不明确:按照说明,您可以使用静态 id 计数器 sprintf("id%d", ++id),这对数十亿个值很有用,或者在 += 'x' 之后返回 std::string ;-)。如果在实践中这些值没有用完,回收可能是一个空操作,或者您可以创建一个 deque 并优先使用它。在长度、格式、性能、跨主机唯一性、跨进程运行唯一性等方面,您真正有哪些其他限制/期望?或者我们应该推断出类似于 Boost::Uuid 目的的需求?
  • 明确地说,我担心整数环绕,因为我正在编写一个长时间运行的服务器。我主要关心的是上面的#2。
  • 您可以轻松地使用无符号 64 位 int,甚至可以使用简单的 if (!++low_order) ++high_order; 链接 2...?在太阳熄灭之前应该没问题....
  • 值得考虑“极不可能”复制意味着什么。维基百科描述了 122 位随机 UUID 的机会“换句话说,只有在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率约为 50%。一个副本的概率约为 50如果地球上每个人都拥有 6 亿个 UUID。” en.wikipedia.org/wiki/Universally_unique_identifier。 IMO 更有可能出现某种类型的硬件故障,从而在您使用的任何精心设计的代码中为您提供副本。
  • @Michael Anderson:我真的希望你不是负责设计飞机、汽车或核能的东西 :-)

标签: c++ uuid idioms


【解决方案1】:

我认为您已经通过查找 Boost::Uuid 为大多数实际目的解决了此问题,但您要求回收已生成的标识符除外。

来自问题中的documentation you linked to

当 UUID 由其中一个生成时 定义的机制,它们是 保证独一无二,与众不同 从所有其他生成的 UUID(即 是,它以前从未生成过 并且永远不会再次生成), 或者极有可能是独一无二的 (取决于机制)。

如果您一心想要回收和重新使用现有标识符,我想您可以随着时间的推移建立一个 UUID 池,仅在您需要一个 UUID 并发现池为空时生成新的。但我无法想象这样的场景比生成新的 UUID 更可取。

编辑:您评论说您需要保证唯一性。实际上,当以编程方式生成唯一标识符时,您永远不会得到一个。在实践中,您将生成的 ID 存储在具有有限大小的数据类型中,因此您可以生成的可能 ID 集也是有限的。恕我直言,你能做到的最好的就是在公差阈值内模拟唯一性。

你可以这样做

  • 使用一种技术,使获得重复 UUID 的机会变得非常遥远(这就是 Boost::UUID 的作用);

  • 将极可能是唯一的 UUID 的生成包装在一些其他逻辑中,该逻辑在已生成的 UUID 列表中查找新生成的 UUID,以消除新生成的 UUID 的微小机会是重复的。显然,当您在列表中处理大量 UUID 时,这样做的实用性会降低。您预计会产生多少?

  • 1234563使用GNU Bignum Library 之类的东西为你做这件事。

【讨论】:

  • 所以在这种情况下,“极有可能”对我不起作用。我确实想要保证。
  • @Andres Jaan Tack:什么样的保证?您是否需要 UUID 生成器更强有力地保证数字是唯一的,而不是从 RAM 中获得它会正确存储它们,而不是翻转几位以将新的唯一值更改为您看到的值前?这段代码是在真机上运行,​​还是图灵机的柏拉图式理想?
  • @razlebe 我喜欢你对这个问题的处理。总之,正确的答案是,“每个人都乐于相信 UUID 生成器,并且一切都很好。”
【解决方案2】:

ID 的有效期是多久?你真的需要回收它们吗,或者你能忍受它们永远独一无二吗?您需要一次生成多少个?你可以为 id 贡献多少位?

这是一个简单的方法:获取您的以太网卡的 MAC 地址(这是全球唯一的硬件问题),混合时间/日期(以毫秒为分辨率)和一个递增的整数计数器(每个生成的 id 递增一次),然后您d 有一个在您的时间/日期范围内唯一的 id,只要您不在这台机器上在一毫秒内生成它们的 MAXINT。现在它不是随机的,攻击者很容易预测,所以不要将它用于安全,它肯定不是最有效的位使用,但它是全球唯一的。

【讨论】:

  • 我所描述的并不是针对任何类型的蓄意攻击的证据。整数计数器将防止正常的时钟更改问题,除非您每天滚动多次。
  • 是的,我所说的并不是批评。只是在使用时间时要记住的一点。
【解决方案3】:

您需要什么样的独特性?
只是在程序的生命周期内是唯一的,还是在多次运行/跨进程中是唯一的?

如果是前者,那么您可以只需new 一个字节的内存,然后使用该内存的地址作为您的标识符。这将保证是唯一的,直到您 delete 内存,此时它可能会被回收。

这可以很容易地包装在这样的类中:

#include <stdint.h>

class UID
{
public:
        typedef uint64_t id_type;

        static const id_type reserve_id()
        {
                uint8_t* idBlock = new uint8_t;
                *idBlock = validId;
                return (id_type)idBlock;
        }

        static void recycle(id_type id)
        {
                uint8_t* idBlock = (uint8_t*)id;
                if (*idBlock == validId)
                {
                        *idBlock = 0;
                        delete idBlock;
                }
        }
private:
        static const uint8_t validId = 0x1D;
};

可能有点不寻常,但如果您只需要每个进程的唯一性,它就可以满足您的要求:)

【讨论】:

    【解决方案4】:

    是的,这很简单。

    1. reserve_id 函数是operator new(0)
    2. 这会分配零字节,但具有唯一的地址。
    3. recycle函数当然是operator delete

    【讨论】:

    • @Salters True - 直到软件重新启动,或者地址用完。
    • 这不是我在您之前 2 小时提出的建议吗? (除了我分配了一个字节,所以我可以添加一些有限的验证)。我的投票该死的在哪里? :)
    • 好吧,我没有费心将其转换为整数类型。如果 UUID 可以接受,void* 可能也可以接受。
    【解决方案5】:

    这个问题似乎与 C++ 无关,它更像是一个基本问题。在任何给定时间预计有多少 ID 是有效的?如果您希望在任何给定时间只有很少的有效 ID,只需将它们放入链表、向量或集合等容器中,具体取决于您的性能要求和相对回收/保留频率。排序链表可能是最好的选择,因为您将在 O(n) 中同时进行回收和保留操作。一个向量有 O(n)、O(n log n) 和一个集合分别有 O(n log n)、O(n)(可能是错的,我很快就想到了)。

    void recycle(ID) {
        container.remove(ID);
        // abort if unsuccessiful (= invalid ID)
    }
    
    ID reserve() {
        static ID last = 0;
        while(container.find(last)) {
            last++;
        }
        return last;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-02
      • 2016-02-18
      • 2022-11-16
      • 1970-01-01
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      相关资源
      最近更新 更多