根据锁定时间锁定资源并生成时间戳

时间:2015-10-24 20:02:22

标签: c# multithreading synchronization critical-section trusted-timestamp

假设我想实现一个同步原语,它生成一个将在同步协议中使用的时间戳。时间戳将是这样的,对于用于锁定资源的给定密钥,没有两个线程能够获得相同的时间戳值。

后一种规范的可能实现方式是:

namespace InfinityLabs.PowersInfinity.BCL.Synchronization
{

public static class TimeStampMonitor
{
    private static readonly IDictionary<object, long> TimeStamps;

    static TimeStampMonitor()
    {
        TimeStamps = new Dictionary<object, long>();
    }

    #region API

    public static long Enter(object key)
    {
        var lockTaken = false;

        Monitor.Enter(key, ref lockTaken);

        ThrowIfLockNotAcquired(key, lockTaken);

        var timeStamp = GetCurrentTimeStamp();

        Thread.Sleep(1);

        TimeStamps.Add(key, timeStamp);

        return timeStamp;
    }

    public static void Exit(object key)
    {
        var lockTaken = false;

        Monitor.Enter(key, ref lockTaken);

        try
        {
            ThrowIfInvalidKey(key);
            TimeStamps.Remove(key);
        }
        finally
        {
            if (lockTaken)
                Monitor.Exit(key);
        }
    }

    public static long GetTimeStampOrThrow(object key)
    {
        TryEnterOrThrow(key);

        var timeStamp = GetTimeStamp(key);
        return timeStamp;
    }

    public static void TryEnterOrThrow(object key)
    {
        var lockTaken = false;

        try
        {
            Monitor.Enter(key, ref lockTaken);

            ThrowIfLockNotAcquired(key, lockTaken);
            ThrowIfInvalidKey(key);
        }
        catch (SynchronizationException)
        {
            throw;
        }
        catch (Exception)
        {
            if(lockTaken)
                Monitor.Exit(key);

            throw;
        }
    }

    #endregion

    #region Time Stamping

    private static long GetCurrentTimeStamp()
    {
        var timeStamp = DateTime.Now.ToUnixTime();
        return timeStamp;
    }

    private static long GetTimeStamp(object key)
    {
        var timeStamp = TimeStamps[key];
        return timeStamp;
    }

    #endregion

    #region Validation

    private static void ThrowIfInvalidKey(object key, [CallerMemberName] string methodName = null)
    {
        if (!TimeStamps.ContainsKey(key))
            throw new InvalidOperationException($"Must invoke '{nameof(Enter)}' prior to invoking '{methodName}'. Key: '{key}'");
    }

    private static void ThrowIfLockNotAcquired(object key, bool lockTaken)
    {
        if (!lockTaken)
            throw new SynchronizationException($"Unable to acquire lock for key '{key}'");
    }
    #endregion
}
}

请注意,两个API方法 TryEnterOrThrow GetTimeStampOrThrow 旨在将类作为保护方法使用,这些方法不允许编写糟糕的代码来破坏临界区的原子性。后一种方法还返回给定密钥的先前获取的时间戳值。时间戳保持很长时间,使其所有者不会退出关键部分。

我一直在脑子里运行所有可能的情景,我似乎无法打破它 - 不仅是原子的,而是试图滥用它。 我想我的问题是,因为这是我编写同步原语的极少数尝试之一 - 这个代码是傻瓜证明它是否提供了原子性?

非常感谢帮助!

1 个答案:

答案 0 :(得分:2)

查看https://stackoverflow.com/a/14369695/224370

您可以创建唯一的时间戳而无需锁定。代码如下。

您的问题略有不同,因为您希望每个密钥具有唯一的时间戳,但如果您拥有全局唯一的时间戳,则每个密钥自动拥有唯一的时间戳,而无需任何额外的工作。所以我不认为你真的需要一本字典:

public class HiResDateTime
{
   private static long lastTimeStamp = DateTime.UtcNow.Ticks;
   public static long UtcNowTicks
   {
       get
       {
           long original, newValue;
           do
           {
               original = lastTimeStamp;
               long now = DateTime.UtcNow.Ticks;
               newValue = Math.Max(now, original + 1);
           } while (Interlocked.CompareExchange
                        (ref lastTimeStamp, newValue, original) != original);

           return newValue;
       }
   }
}