タイムスタンプを使用して、プログラム内の同時変更を一時的に順序付けています。変更の各タイムスタンプは一意である必要があります。ただし、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秒先に進んだだけでした。実際のアプリケーションでは、スキューは目立たなくなります。
重複のないタイムスタンプの厳密な昇順シーケンスを取得する1つの方法は、次のコードです。
ここの他の回答と比較すると、これには次の利点があります。
lock
ステートメントを使用するソリューションよりも優れたパフォーマンスを発揮するはずです。。
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;
}
}
}
えーと、あなたの質問への答えは「できない」ということです。なぜなら、2つの操作が同時に発生する場合(マルチコアプロセッサでそれらが発生する場合)、精度に関係なく、同じタイムスタンプを持つからです。ギャザー。
とはいえ、自動インクリメントするスレッドセーフカウンターが必要なようです。これを(おそらくグローバルサービスとして、おそらく静的クラスで)実装するには、 Interlocked.Increment
メソッド、そしてあなたがあなたが決定した場合はint.MaxValue
可能なバージョン、また Interlocked.Read
。
DateTime.Nowは10〜15ミリ秒ごとにのみ更新されます。
それ自体はだまされたものではありませんが、このスレッドには、重複を減らしたり、より良いタイミング解決を提供するためのいくつかのアイデアがあります。
。NET/C#でティック精度のタイムスタンプを取得するには?
つまり、タイムスタンプは情報にとって恐ろしいキーです。物事がそれほど速く発生する場合は、発生時にアイテムの個別の順序を維持するインデックス/カウンターが必要な場合があります。そこには曖昧さはありません。
最も簡単な方法は、タイムスタンプとアトミックカウンターを組み合わせることです。タイムスタンプの解像度が低いという問題はすでにわかっています。アトミックカウンターを単独で使用すると、アプリケーションを停止して開始する場合に状態を保存する必要があるという単純な問題もあります(そうでない場合、カウンターは0から始まり、重複が発生します)。
一意のIDを取得するだけの場合は、タイムスタンプとカウンタ値を区切り文字で連結するのと同じくらい簡単です。ただし、値を常に正しい順序にしたいので、それだけでは不十分です。基本的には、アトミックカウンター値を使用してタイムスタンプに固定幅の精度を追加するだけです。私はJava開発者なので、まだC#サンプルコードを提供することはできませんが、問題は両方のドメインで同じです。以下の一般的な手順に従ってください:
この戦略は、1ミリ秒あたり6666よりも速いIDが必要でない限り機能し(15ミリ秒が最も細かい解像度であると想定)、アプリケーションの再起動時に状態を保存する必要なく常に機能します。
一意であるとは保証できませんが、おそらく ticks の使用は十分に細かいですか?
1つのティックは100ナノ秒または1千万分の1秒を表します。 1ミリ秒に10,000ティックあります。
完全に何をしようとしているのかわからないが、おそらくキューを使用して順次処理レコードを処理することを検討する。