0

同期プロトコルで使用されるタイムスタンプを生成する同期プリミティブを実装したいとします。タイムスタンプは、リソースをロックするために使用される特定のキーの場合、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 メソッドTryEnterOrThrowGetTimeStampOrThrowは、不適切に記述されたコードがクリティカル セクションの原子性を壊すことを禁止するガード メソッドとしてクラスを消費することによって使用されることを意図していることに注意してください。後者のメソッドは、特定のキーに対して以前に取得したタイム スタンプ値も返します。タイム スタンプは、その所有者がクリティカル セクションを終了しないように長期間維持されます。

私は考えられるすべてのシナリオを頭の中で実行してきましたが、それを壊すことはできないようです-原子的にだけでなく、それを悪用しようとしても. 私の質問は、これは同期プリミティブを作成するための私の数少ない試みの 1 つであるため、このコードは誰にでもわかるものであり、アトミック性を提供しますか?

助けていただければ幸いです。

4

1 に答える 1

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;
       }
   }
}
于 2015-10-24T20:08:34.450 に答える