1

I have the following code. In windows server 2008, the program is correct and runs as expected. It outputs 10 different ids.

However, when I run it in windows server 2003, the program is not correct. It outputs 10 ids, but some of the id's are duplicates. It seems that the lock is not working.

If I set Thread.Sleep(500), it works correctly on windows server 2003.

class Test
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(Util.GetId());
        }
        Console.ReadLine();
    }
}

public class Util
{
    private static object idlock = new object();

    public static string GetId()
    {
        string id = "";
        lock (idlock)
        {
            Thread.Sleep(1);
            id = System.DateTime.Now.ToString("yyMMddHHmmssffff");
        }
        return id;
    }
}
Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
Allen Fu
  • 66
  • 1
  • 7
  • I think the problem is : Windows is not a real-time OS, so you will not get 1000 wakes per second from Thread.Sleep(1). If you haven't used timeBeginPeriod to set your minimum resolution you'll wake about every 15 ms. Even after you've set the minimum resolution to 1 ms, you'll still only wake up every 3-4 ms. In order to get millisecond level timer granularity you have to use the Win32 multimedia timer (C# wrapper). – Allen Fu Apr 22 '11 at 06:25
  • Now,I have set the sleep time to 15 and the problem is resolved when the application run in windows 2003.But it still run correctly in windows 2008.Thanks for all of your reply! – Allen Fu Apr 22 '11 at 06:30

5 Answers5

3

Locking is completely unnecessary; the problem is that DateTime.Now only has a granularity of ~15ms or so (depending on your system). Don't use time as an id in the first place; you could easily just do something like this instead:

public static class Util
{
    static long _id;
    public static string GetId()
    {
        return Next().ToString("0000000000000000");
    }

    private static long Next()
    {
        return Interlocked.Increment(ref _id);
    }
}
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
2

While I wold suggest getting away from this method of generating keys, if you are constrained by other conditions then one way you could fix your code without forcing a wait is to keep a reference to the last generated id, then while the just generated id still matches the old one, sleep(10) or so.

This would keep all the existing properties of your code, I think the main one being that it will keep incrementing between runs of the program, not a single session like the other examples presented.

Chris Sainty
  • 9,086
  • 1
  • 26
  • 31
1

Thread.Sleep's parameter is in milliseconds, pick a bigger number. Or use Guid.NewGuid() to generate an actually unique ID without wasting time.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • I know this.But Guid.NewGuid can not use to order. If pick a bigger number,that wasting time. In my application,that is my core method to get a newid. – Allen Fu Apr 22 '11 at 03:18
  • This code will generate a sequential Guid: http://developmenttips.blogspot.com/2008/03/generate-sequential-guids-for-sql.html – Chris Kooken Apr 22 '11 at 03:21
  • My application is running some days,all of the key id in the sqldata is get by this method.so i can not replace the method. – Allen Fu Apr 22 '11 at 03:25
1

Would DateTime.Ticks work for you? It's got 100-nanosecond resolution, so it sounds like it may work. You won't have to lock anything either, IMO.

dawebber
  • 3,503
  • 1
  • 16
  • 16
  • DateTime.Ticks? DateTime has no this property in System.DateTime. – Allen Fu Apr 22 '11 at 03:40
  • Been there since 1.1 [MSDN says](http://msdn.microsoft.com/en-us/library/system.datetime.ticks\(v=VS.71\).aspx) – dawebber Apr 22 '11 at 03:45
  • Just let me be clear: this isn't a static property on DateTime class, so DateTime.Now.Ticks is what you are really after, methinks. – dawebber Apr 22 '11 at 03:51
  • 2
    You're forgetting about the time change - in Fall, you will see the same Ticks for an hour – Ana Betts Apr 22 '11 at 06:04
  • @Paul, yes, this will have to be considered. – dawebber Apr 22 '11 at 06:05
  • 2
    Ticks might have 100 nanosecond resolution, but that doesn't mean that the clock has that level of precision! The clock hardware is only accurate to about 1/64th of a second. – Eric Lippert Apr 22 '11 at 06:10
  • @Eric-true, but we are not trying to measure time, we are trying to generate consecutive numbers to use as keys, from what OP seems to suggest. So, it's not about knowing exactly what time it is to a tick, it's about getting ticks in increasing order. Are you seeing problem with that? Also, isn't the clock precision somewhat hardware-dependent? The more powerful the CPU, the more "accurate" the clock? – dawebber Apr 22 '11 at 06:17
  • 1
    @dawebber: The clock precision is not hardware dependent; the precision of the StopWatch class is hardware dependent; it asks the chip for a high-resolution timer. The precision of the "wall" clock is I think based on the size of the thread quantum. But ignoring all that: you have no guarantee whatsoever that "ticks" is monotone increasing. The user could be messing around with the clock, the time could be changing due to daylight savings time, and so on. If you want a monotone-increasing number **write one yourself**, it is not hard to do. – Eric Lippert Apr 29 '11 at 16:08
  • @Eric, thank you for the clarification about the StopWatch versus the "wall clock" implementation--something new I've learned too from this question. – dawebber Apr 29 '11 at 20:53
0

Now that you're locking, try to save off the prior value, and compare for equality. If equals, don't return, but rather Sleep and try again, in a loop.

Personally, I think that this approach sucks. Use GUIDs, they are your friend.

GregC
  • 7,737
  • 2
  • 53
  • 67