假设我想实现一个同步原语,它生成一个将在同步协议中使用的时间戳。时间戳将是这样的,对于用于锁定资源的给定密钥,没有两个线程能够获得相同的时间戳值。
后一种规范的可能实现方式是:
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 旨在将类作为保护方法使用,这些方法不允许编写糟糕的代码来破坏临界区的原子性。后一种方法还返回给定密钥的先前获取的时间戳值。时间戳保持很长时间,使其所有者不会退出关键部分。
我一直在脑子里运行所有可能的情景,我似乎无法打破它 - 不仅是原子的,而是试图滥用它。 我想我的问题是,因为这是我编写同步原语的极少数尝试之一 - 这个代码是傻瓜证明它是否提供了原子性?
非常感谢帮助!
答案 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;
}
}
}