锁定资源并根据锁定时间生成时间戳
Locking a Resource and generating Time Stamps according to lock time
假设我想实现一个同步原语,它生成一个将在同步协议中使用的时间戳。时间戳是这样的,对于用于锁定 资源 的给定 key,没有两个线程能够获得相同的时间戳值。
后一个规范的可能实现是:
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 旨在通过消费 类作为保护方法,它不允许编写不当的代码破坏临界区的原子性。后一种方法还 returns 先前为给定密钥获取的时间戳值。时间戳保留的时间很长,以至于它的所有者没有退出临界区。
我已经 运行 想过所有可能的场景,但我似乎无法打破它——不仅是原子地,而且是试图滥用它。
我想我的问题是,因为这是我为数不多的编写同步原语的尝试之一——这段代码是万无一失的吗,它是否提供原子性?
不胜感激!
看看
您可以在不锁定的情况下创建唯一的时间戳。下面的代码。
您的问题略有不同,因为您希望每个键都有一个唯一的时间戳,但是如果您有一个全局唯一的时间戳,那么您会自动为每个键分配一个唯一的时间戳,而无需任何额外的努力。所以我认为你真的不需要字典:
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;
}
}
}
假设我想实现一个同步原语,它生成一个将在同步协议中使用的时间戳。时间戳是这样的,对于用于锁定 资源 的给定 key,没有两个线程能够获得相同的时间戳值。
后一个规范的可能实现是:
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 旨在通过消费 类作为保护方法,它不允许编写不当的代码破坏临界区的原子性。后一种方法还 returns 先前为给定密钥获取的时间戳值。时间戳保留的时间很长,以至于它的所有者没有退出临界区。
我已经 运行 想过所有可能的场景,但我似乎无法打破它——不仅是原子地,而且是试图滥用它。 我想我的问题是,因为这是我为数不多的编写同步原语的尝试之一——这段代码是万无一失的吗,它是否提供原子性?
不胜感激!
看看
您可以在不锁定的情况下创建唯一的时间戳。下面的代码。
您的问题略有不同,因为您希望每个键都有一个唯一的时间戳,但是如果您有一个全局唯一的时间戳,那么您会自动为每个键分配一个唯一的时间戳,而无需任何额外的努力。所以我认为你真的不需要字典:
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;
}
}
}