web-dev-qa-db-ja.com

タイムスタンプを常に一意にする方法は?

タイムスタンプを使用して、プログラム内の同時変更を一時的に順序付けています。変更の各タイムスタンプは一意である必要があります。ただし、DateTime.Nowを呼び出すだけでは不十分です。連続して呼び出された場合、同じ値が返されることが多いためです。

私はいくつかの考えを持っていますが、これに対する「最善の」解決策として何も私を襲いません。連続する各呼び出しが一意のDateTimeを生成することを保証する私が書くことができるメソッドはありますか?

おそらくこれに別のタイプを使用する必要がありますか?おそらくlong int? DateTimeには、たとえばインクリメンタルカウンターとは異なり、リアルタイムとして簡単に解釈できるという明らかな利点があります。

pdate:メソッドが呼び出されるたびに一意性を確保しながら、DateTimeを一時キーとして使用できるようにする単純な妥協ソリューションとしてコーディングした結果は次のとおりです。

private static long _lastTime; // records the 64-bit tick value of the last time
private static object _timeLock = new object();

internal static DateTime GetCurrentTime() {
    lock ( _timeLock ) { // prevent concurrent access to ensure uniqueness
        DateTime result = DateTime.UtcNow;
        if ( result.Ticks <= _lastTime )
            result = new DateTime( _lastTime + 1 );
        _lastTime = result.Ticks;
        return result;
    }
}

各ティック値は1秒の1000万分の1なので、このメソッドは、1秒あたり1000万回のオーダーで呼び出された場合に顕著なクロックスキューのみを導入します(ちなみに、これは実行するのに十分効率的です)。私の目的には完全に受け入れられます。

ここにいくつかのテストコードがあります:

DateTime start = DateTime.UtcNow;
DateTime prev = Kernel.GetCurrentTime();
Debug.WriteLine( "Start time : " + start.TimeOfDay );
Debug.WriteLine( "Start value: " + prev.TimeOfDay );
for ( int i = 0; i < 10000000; i++ ) {
    var now = Kernel.GetCurrentTime();
    Debug.Assert( now > prev ); // no failures here!
    prev = now;
}
DateTime end = DateTime.UtcNow;
Debug.WriteLine( "End time:    " + end.TimeOfDay );
Debug.WriteLine( "End value:   " + prev.TimeOfDay );
Debug.WriteLine( "Skew:        " + ( prev - end ) );
Debug.WriteLine( "GetCurrentTime test completed in: " + ( end - start ) );

...そして結果:

Start time:  15:44:07.3405024
Start value: 15:44:07.3405024
End time:    15:44:07.8355307
End value:   15:44:08.3417124
Skew:        00:00:00.5061817
GetCurrentTime test completed in: 00:00:00.4950283

つまり、言い換えると、0.5秒で1000万uniqueのタイムスタンプが生成され、最終結果は0.5秒先に進んだだけでした。実際のアプリケーションでは、スキューは目立たなくなります。

31
devios1

重複のないタイムスタンプの厳密な昇順シーケンスを取得する1つの方法は、次のコードです。

ここの他の回答と比較すると、これには次の利点があります。

  1. 値は実際のリアルタイム値と密接に追跡します(リアルタイムよりもわずかに先に進む非常に高い要求率の極端な状況を除きます)。
  2. それはロックフリーであり、lockステートメントを使用するソリューションよりも優れたパフォーマンスを発揮するはずです。
  3. 昇順を保証します(ループを追加するだけではカウンタは保証されません)。

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;
       }
   }
}
68
Ian Mercer

えーと、あなたの質問への答えは「できない」ということです。なぜなら、2つの操作が同時に発生する場合(マルチコアプロセッサでそれらが発生する場合)、精度に関係なく、同じタイムスタンプを持つからです。ギャザー。

とはいえ、自動インクリメントするスレッドセーフカウンターが必要なようです。これを(おそらくグローバルサービスとして、おそらく静的クラスで)実装するには、 Interlocked.Increment メソッド、そしてあなたがあなたが決定した場合はint.MaxValue可能なバージョン、また Interlocked.Read

7
Domenic

DateTime.Nowは10〜15ミリ秒ごとにのみ更新されます。

それ自体はだまされたものではありませんが、このスレッドには、重複を減らしたり、より良いタイミング解決を提供するためのいくつかのアイデアがあります。

。NET/C#でティック精度のタイムスタンプを取得するには?

つまり、タイムスタンプは情報にとって恐ろしいキーです。物事がそれほど速く発生する場合は、発生時にアイテムの個別の順序を維持するインデックス/カウンターが必要な場合があります。そこには曖昧さはありません。

6
Joe

最も簡単な方法は、タイムスタンプとアトミックカウンターを組み合わせることです。タイムスタンプの解像度が低いという問題はすでにわかっています。アトミックカウンターを単独で使用すると、アプリケーションを停止して開始する場合に状態を保存する必要があるという単純な問題もあります(そうでない場合、カウンターは0から始まり、重複が発生します)。

一意のIDを取得するだけの場合は、タイムスタンプとカウンタ値を区切り文字で連結するのと同じくらい簡単です。ただし、値を常に正しい順序にしたいので、それだけでは不十分です。基本的には、アトミックカウンター値を使用してタイムスタンプに固定幅の精度を追加するだけです。私はJava開発者なので、まだC#サンプルコードを提供することはできませんが、問題は両方のドメインで同じです。以下の一般的な手順に従ってください:

  1. 0〜99999の範囲で循環するカウンター値を提供する方法が必要になります。 100000は、ミリ秒の精度のタイムスタンプを64ビット長の固定幅の値と連結するときに可能な値の最大数です。したがって、基本的には、単一のタイムスタンプ解像度(15ミリ秒程度)内で100,000を超えるIDが必要になることはないと想定しています。 Interlockedクラスを使用してアトミックなインクリメントを提供し、0にリセットする静的メソッドが理想的な方法です。
  2. 次に、IDを生成するために、タイムスタンプを5文字まで埋め込んだカウンター値と連結します。したがって、タイムスタンプが13023991070123でカウンタが234の場合、IDは1302399107012300234になります。

この戦略は、1ミリ秒あたり6666よりも速いIDが必要でない限り機能し(15ミリ秒が最も細かい解像度であると想定)、アプリケーションの再起動時に状態を保存する必要なく常に機能します。

3
Justin Waugh

一意であるとは保証できませんが、おそらく ticks の使用は十分に細かいですか?

1つのティックは100ナノ秒または1千万分の1秒を表します。 1ミリ秒に10,000ティックあります。

1
Giovanni Galbo

完全に何をしようとしているのかわからないが、おそらくキューを使用して順次処理レコードを処理することを検討する。

0
mservidio