【问题标题】:12 Digit Unique ID - Code reliability12 位唯一 ID - 代码可靠性
【发布时间】:2010-10-15 09:44:51
【问题描述】:

我想要一个在一天(24 小时)内保持唯一的号码。以下是我想出的代码;我想知道它的谬误/可能的风险; “我相信”这保证了至少一天的 12 位唯一编号。

逻辑是获取当前日期/时间(hhmmssmmm)并连接查询性能计数器结果的前四个字节。

__forceinline bool GetUniqueID(char caUID[MAX_STRING_LENGTH])
{
    //Logic: Add HHMMSSmmm with mid 3 bytes of performance counter.
    //Guarantees that in a single milli second band (0 to 999) the three bytes 
    //of performance counter would always be unique.
    //1. Get system time, and use
    bool bStatus = false;
    try
    {

        SYSTEMTIME localtime;
        GetLocalTime(&localtime);//Get local time, so that we may pull out HHMMSSmmm

        LARGE_INTEGER li;
        char cNT[MAX_STRING_LENGTH];//new time.
        memset(cNT, '\0', sizeof(cNT));
        try
        {
            //Try to get the performance counter,
            //if one is provided by the OEM.

            QueryPerformanceCounter(&li);//This function retrieves the current value of the 
                                         //high-resolution performance counter if one is provided by the OEM
                                         //We use the first four bytes only of it.
            sprintf(cNT, "%u", li.QuadPart);
        }
        catch(...)
        {
            //Not provided by OEM.
            //Lets go with the GetTickCounts();
            //ddHHMMSS + 4 bytes of dwTicks
            sprintf(cNT,"%04d", GetTickCount());
        }


        //Get the first four bytes.
        int iSkipTo     = 0;//This is incase we'd decide to pull out next four bytes, rather than first four bytes.
        int iGetChars   = 4;//Number of chars to get.
        char *pSub = (char*) malloc(iGetChars+1);//Clear memory
        strncpy(pSub, cNT + iSkipTo, iGetChars);//Get string
        pSub[iGetChars] = '\0'; //Mark end.

        //Prepare unique id
        sprintf(caUID, "%02d%02d%02d%3d%s", 
                                    localtime.wHour, 
                                    localtime.wMinute, 
                                    localtime.wSecond, 
                                    localtime.wMilliseconds, 
                                    pSub); //First four characters concat.

        bStatus = true;
    }
    catch(...)
    {
        //Couldnt prepare. There was some problem.
        bStatus = false;
    }

    return bStatus;
}

以下是我得到的输出:

独特:[125907 462224] 唯一:[125907 462225] 唯一:[125907 462226] 唯一:[125907 462227] 唯一:[125907 462228] 唯一:[125907 462230] 唯一:[125907 462231] 唯一:[125907 462232] 唯一:[125907 462233] 唯一:[125907 462234] 唯一:[125907 462235] 唯一:[125907 462237] 唯一:[125907 462238] 唯一:[125907 462239] 唯一:[125907 462240] 唯一:[125907 462241] 唯一:[125907 462243] 唯一:[125907 462244] 唯一:[125907 462245] 唯一:[125907 462246] 唯一:[125907 462247] 唯一:[125907 462248] 唯一:[125907 462249] 唯一:[125907 462251] 唯一:[125907 462252] 唯一:[125907 462253] 唯一:[125907 462254] 唯一:[125907 462255] 唯一:[125907 462256] 唯一:[125907 462257] 唯一:[125907 462258] 毫秒变化,46 唯一:[125907 622261] 唯一:[125907 622262] 唯一:[125907 622263] 唯一:[125907 622264] 唯一:[125907 622265] 唯一:[125907 622267] 唯一:[125907 622268] 唯一:[125907 622269] 唯一:[125907 622270] 唯一:[125907 622271] 唯一:[125907 622273] 唯一:[125907 622274] 唯一:[125907 622275] 唯一:[125907 622276] 唯一:[125907 622277] 唯一:[125907 622278] 唯一:[125907 622279] 唯一:[125907 622281] 唯一:[125907 622282] 唯一:[125907 622283] 唯一:[125907 622284] 唯一:[125907 622285] 唯一:[125907 622286] 唯一:[125907 622288] 唯一:[125907 622289] 唯一:[125907 622290] 唯一:[125907 622291] 唯一:[125907 622292] 唯一:[125907 622293] 唯一:[125907 622295] 唯一:[125907 622296] 唯一:[125907 622297] 唯一:[125907 622298] 唯一:[125907 622299] 唯一:[125907 622300] 唯一:[125907 622301] 唯一:[125907 622302] 唯一:[125907 622304] 唯一:[125907 622305] 唯一:[125907 622306] 毫秒变化,62 唯一:[125907 782308] 唯一:[125907 782310] 唯一:[125907 782311] 唯一:[125907 782312] 唯一:[125907 782313] 唯一:[125907 782314] 唯一:[125907 782316] 唯一:[125907 782317] 唯一:[125907 782318] 唯一:[125907 782319] 毫秒变化,125 唯一:[1259071402495] 唯一:[1259071402497] 唯一:[1259071402498] 唯一:[1259071402499] 唯一:[1259071402500] 唯一:[1259071402502] 唯一:[1259071402503] 唯一:[1259071402504] 唯一:[1259071402505] 唯一:[1259071402507]

现在我正在考虑将生成的 ID 保存在一个列表中,并将新生成的 ID 与列表中的现有 ID 进行比较。如果它已经存在于列表中,那么我当然可以跳过这个数字并生成另一个,但是这个逻辑肯定会失败。

非常感谢您的 cmets/建议/更新/等。

谢谢 JT。

【问题讨论】:

  • 为什么 malloc psub 为 5 个字节,而不是可以用 psub[5]={0}; 进行初始化;
  • 拉斯赫曼,感谢您的建议。感谢您的回复。

标签: c++ unique uniqueidentifier


【解决方案1】:

我的解决方案是获取系统时间并为其添加一个计数器(伪代码):

static int counter = 0;
static Time lastTime;

String getNextId() {
    Time now = System.getTime();
    if (lastTime == now)
        counter ++;
    else
        counter = 0;
    return now+counter;
}

这将保证即使我调用该方法的频率高于getTime() 的更改次数,我也会获得一个新的 ID。

【讨论】:

  • 我的方法的好处是我只需要记住最后的结果。
【解决方案2】:

如果您是在单核处理器上的一台机器上运行它,那么逻辑对我来说似乎是合理的。但是,我不知道对于连续调用的多核处理器是否同样适用。但是,生成的数字在机器之间肯定不会是唯一的。

出于好奇,您不使用 GUID 是否有原因?

或者,既然您正在考虑将生成的 ID 保存在一个列表中进行比较,为什么不能创建一个生成器?由于您建议存储是一种选择,如果您存储最后使用的数字并在每次使用时递增它...您甚至可以存储一个日期并根据需要每天重置计数器。

【讨论】:

  • 谢谢 LC:虽然你对生成器的想法很好;它也可以工作。我想要一个列表的原因是查看并验证新号码是否在当天已经出现。在生产中保留以前数字的列表将/可能意味着列表长度未知,并且可能存在内存泄漏。
  • 这就是为什么如果你可以保留一个本地生成器,你不需要知道最后生成的数字。也就是说,当然,如果您的机器是唯一生成数字的机器。
【解决方案3】:

我遇到了快速循环中的时间问题,尽管它们应该是常识,但它们没有改变(你无法控制操作系统,所以你不能假设时间每 x 毫秒改变一次等)

将计数器值添加为额外的几位数字(而不是增量)并在重新启动时或在 9999 之后将其重置应该足以隐藏这一点,使其几乎不可能发生(著名的遗言)。

格式然后是 TTTTTToooo,其中 T 是您的时间数字和 o 您的 4 位偏移量。

【讨论】:

    【解决方案4】:

    Aaron:感谢你们的 cmets,我用你们的方法得到了我想要的。

    __forceinline bool GetUniqueIDEx(char caUID[MAX_STRING_LENGTH])
    {
        //Logic: Add HHMMSSmmm with 3 bytes counter.
        //Guarantees a unique number for a calendar date, 
        //that in a single milli second band (0 to 999) the three bytes 
        //of counter would always be unique.
        //1. Get system time, and use
    
        bool bStatus = false;
        try
        {
            GetLocalTime(&localtime);//Get local time, so that we may pull out HHMMSSmmm
    
            char cNT[MAX_STRING_LENGTH];//new time.
            memset(cNT, '\0', sizeof(cNT));
            if(m_nCounter> MAX_COUNTER_LIMIT)
            {
                m_nCounter= 0;
            }
    
            sprintf(cNT, "%03d", ++m_nCounter);
    
            //Prepare unique id
            sprintf(caUID, "%02d%02d%02d%03d%s", 
                                                localtime.wHour, 
                                                localtime.wMinute, 
                                                localtime.wSecond, 
                                                localtime.wMilliseconds, 
                                                cNT); 
    
            bStatus = true;
        }
        catch(...)
        {
            //Couldnt prepare. There was some problem.
            bStatus = false;
        }
    
        return bStatus;
    }
    

    【讨论】:

      【解决方案5】:

      我会使用午夜后的秒数,然后使用计数器。这可以为您提供高达每秒一千万次点击。应该很独特。使用上面 Aaron 的代码,您可以将字符串格式化为:

      sprintf(idstr, "%05d%07d", secs_since_midnight, counter);
      

      当然,当您真的想将计数器塞入几个可打印字符(以及年中的某天而不是月/日等)时,我也坚信使用 base-36(通过 itoa)

      【讨论】:

        【解决方案6】:

        我不确定您的流程是否可以牺牲微小的性能问题并保持逻辑简单。

        如果您在两次调用之间让您的进程休眠 1 毫秒,我们可以保证 HHMMSSmmm 格式本身的唯一编号。通过这种方式,您可以消除连接部分以及您必须维护的列表以仔细检查唯一性。

        马尼坎坦·维拉尤瑟姆 // 编程为大思考

        【讨论】:

        • 当您的应用程序期望在一毫秒甚至一秒内有超过 1000 个请求时会发生什么?并且用户正在等待响应,而后者又将响应其他系统 - 依此类推。你相信增加睡眠会有所帮助吗?虽然您的建议可能有效,但我相信没有系统“应该”允许睡眠呼叫只是为了获得一个独特的计时器。一个不太好的设计。
        猜你喜欢
        • 1970-01-01
        • 2011-11-03
        • 1970-01-01
        • 2015-01-10
        • 2011-02-19
        • 1970-01-01
        • 1970-01-01
        • 2013-05-04
        • 2012-04-05
        相关资源
        最近更新 更多