同期プロトコルで使用されるタイムスタンプを生成する同期プリミティブを実装したいとします。タイムスタンプは、リソースをロックするために使用される特定のキーの場合、2 つのスレッドが同じタイムスタンプ値を取得できないようなものです。
後者の仕様の可能な実装は次のとおりです。
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
}
}
2 つの API メソッドTryEnterOrThrowとGetTimeStampOrThrowは、不適切に記述されたコードがクリティカル セクションの原子性を壊すことを禁止するガード メソッドとしてクラスを消費することによって使用されることを意図していることに注意してください。後者のメソッドは、特定のキーに対して以前に取得したタイム スタンプ値も返します。タイム スタンプは、その所有者がクリティカル セクションを終了しないように長期間維持されます。
私は考えられるすべてのシナリオを頭の中で実行してきましたが、それを壊すことはできないようです-原子的にだけでなく、それを悪用しようとしても. 私の質問は、これは同期プリミティブを作成するための私の数少ない試みの 1 つであるため、このコードは誰にでもわかるものであり、アトミック性を提供しますか?
助けていただければ幸いです。