J'utilise des horodatages pour ordonner temporairement les modifications simultanées de mon programme et exige que chaque horodatage d'un changement soit unique. Cependant, j'ai découvert qu'appeler simplement DateTime.Now
est insuffisant, car il retournera souvent la même valeur s'il est appelé successivement.
J'ai quelques idées, mais rien ne me semble être la "meilleure" solution à ce problème. Existe-t-il une méthode que je peux écrire qui garantisse que chaque appel successif produit une DateTime
unique?
Devrais-je peut-être utiliser un type différent pour cela, peut-être un long int? DateTime
a l'avantage évident d'être facilement interprétable en temps réel, contrairement à un compteur incrémentiel.
Mise à jour: Voici ce que j'ai fini par coder comme une solution de compromis simple qui me permet encore d'utiliser DateTime
comme clé temporelle, tout en garantissant l'unicité à chaque appel de la méthode:
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;
}
}
Comme chaque valeur de tick ne représente qu'un 10 millionième de seconde, cette méthode introduit uniquement un décalage d'horloge notable lorsqu'elle est appelée de l'ordre de 10 millions de fois par seconde (ce qui est d'ailleurs suffisamment efficace pour s'exécuter à), ce qui signifie parfaitement acceptable pour mes besoins.
Voici un code de test:
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 ) );
... et les résultats:
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
En d’autres termes, en une demi-seconde, il a généré 10 millions unique / horodatage et le résultat final n’a été avancé que d’une demi-seconde. Dans les applications réelles, l'inclinaison serait imperceptible.
Le code suivant constitue un moyen d'obtenir une séquence d'horodatage strictement ascendante sans doublons.
Comparé aux autres réponses ici, celui-ci présente les avantages suivants:
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;
}
}
}
Euh, la réponse à votre question est que "vous ne pouvez pas", car si deux opérations sont effectuées en même temps (ce qui sera le cas dans les processeurs multicœurs), elles auront le même horodatage, quelle que soit la précision que vous parviendrez à obtenir. recueillir.
Cela dit, cela ressemble à ce que vous voulez, c'est une sorte de compteur thread-safe auto-incrémenté. Pour implémenter cela (probablement en tant que service global, peut-être dans une classe statique), vous utiliseriez la méthode Interlocked.Increment
, et si vous décidiez de prendre plus de int.MaxValue
versions possibles, également Interlocked.Read
.
DateTime.Now n'est mis à jour que toutes les 10-15ms.
Ce n'est pas une dupe en soi, mais ce fil a quelques idées sur la réduction des doublons/fournissant une meilleure résolution temporelle:
Comment obtenir l'horodatage de la précision des ticks dans .NET/C #?
Cela étant dit: les horodatages sont des clés horribles pour l’information; si les choses se passent aussi vite, vous voudrez peut-être un index/compteur qui garde l'ordre discret des éléments au fur et à mesure de leur apparition. Il n'y a pas d'ambiguïté là-bas.
Je trouve que le moyen le plus sûr est de combiner un horodatage et un compteur atomique. Vous connaissez déjà le problème de la mauvaise résolution d'un horodatage. L’utilisation d’un compteur atomique à lui seul pose également le simple problème de demander que son état soit stocké si vous voulez arrêter et démarrer l’application (sinon, le compteur recommence à 0, ce qui entraîne des doublons).
Si vous ne recherchiez qu'un identifiant unique, ce serait aussi simple que de concaténer la valeur de l'horodatage et du compteur avec un séparateur. Mais comme vous voulez que les valeurs soient toujours en ordre, cela ne suffira pas. Fondamentalement, tout ce que vous avez à faire est d’utiliser la valeur du compteur atomique pour ajouter une précision supplémentaire de largeur fixe à votre horodatage. Je suis un développeur Java, donc je ne pourrai pas encore fournir d’exemple de code C #, mais le problème est le même dans les deux domaines. Il suffit donc de suivre ces étapes générales:
Cette stratégie fonctionne tant que vous n'avez pas besoin d'identifiants plus rapides que 6666 par ms (en supposant que la résolution la plus granulaire soit 15ms) et fonctionne toujours sans qu'il soit nécessaire de sauvegarder un état lors des redémarrages de votre application.
Il ne peut pas être garanti d'être unique, mais peut-être que l'utilisation de ticks est suffisamment granulaire?
Un tick simple représente cent Nanosecondes ou un dix millionième de Seconde. Il y a 10 000 ticks en Milliseconde.
Vous ne savez pas trop ce que vous essayez de faire, mais envisagez peut-être d'utiliser des files d'attente pour traiter les enregistrements de manière séquentielle.