c# - ¿Cuál es la fuente de marca de tiempo más estable para una aplicación .NET?


1 Answers

No veo ningún problema con el uso de Environment.TickCount . La documentación indica que se devuelve:

Un entero con signo de 32 bits que contiene la cantidad de tiempo en milisegundos que ha transcurrido desde la última vez que se inició el equipo.

Sigue diciendo:

El valor de esta propiedad se deriva del temporizador del sistema y se almacena como un entero con signo de 32 bits.

No veo cómo la corrección de tiempo NTP puede afectar esto. ¿Tienes un enlace que sugiere lo contrario? La parte difícil es lidiar con el envolvente. Por lo tanto, al comparar el recuento de tics actual con el anterior, si obtiene un número negativo, sabrá que se produjo una reinversión.

Personalmente, creo que este es el mecanismo superior para usar, porque el valor de retorno es en milisonds, por lo que el factor de conversión para su tiempo de espera será sencillo. De lo contrario, con Stopwatch.GetTimestamp tendrás que hacer un trabajo adicional para tener en cuenta la frecuencia de tic.

Además, GetTickCount esto se implementa internamente con una llamada a la función GetTickCount Win32. (dotPeek muestra que está marcado con MethodImplOptions.InternalCall , así que no puedo asegurarlo) Pero la documentación para eso dice:

Observaciones

La resolución de la función GetTickCount está limitada a la resolución del temporizador del sistema, que generalmente está en el rango de 10 milisegundos a 16 milisegundos. La resolución de la función GetTickCount no se ve afectada por los ajustes realizados por la función GetSystemTimeAdjustment .

Pensándolo bien, ya que solo verifica periódicamente cada segundo, la resolución del temporizador solo tiene que ser <1 seg. No hay ningún punto en intentar hacerlo mejor, así que ¿por qué usar cualquier llamada a la API?

private int m_conTimer;

void OneSecondThreadCallback() {
    if (++m_conTimer >= TIMEOUT_VALUE)
        // Connection timed out. React accordingly.
}
c# datetime

Tema base

Tengo un problema con las marcas de tiempo en mi aplicación de C #. Recibo datos de una conexión TCP remota de forma asíncrona. Cada vez que recibo datos, actualizo una variable de marca de tiempo a DateTime.Now . En un hilo separado, una vez por segundo, verifico si ha transcurrido más de un período de tiempo de espera predefinido desde mi última recepción y, en ese caso, desconectar. Este método ha estado funcionando durante muchos años, pero ahora tengo una situación en la que la aplicación está instalada en una máquina con una fuente de tiempo inestable. Cada pocos días, el tiempo de la máquina se "auto-corrige" y descarto la conexión prematuramente. El código es básicamente el siguiente:

Proceso de recepción

void OnReceiveComplete(IAsyncResult ar) {
    ...
    mySocket.EndReceive(ar);
    lastRxTime = DateTime.Now;
    ...
}

Proceso de verificación

void CheckConnection() {
    TimeSpan ts = DateTime.Now.Subtract(lastRxTime);
    if(ts.TotalMilliseconds > timeout) {
        Disconnect(string.Format("No packet received from the server for over {0} seconds.", timeout / 1000));
    }
}

Tengo capturas válidas de Wireshark durante el problema y, justo antes de la desconexión, veo tráfico NTP que culmina en lo que parece una corrección de al menos 1 minuto. Esto obviamente hace que el proceso de verificación falle.

Detalles técnicos / Respuestas a preguntas esperadas

  1. Tengo el control de ambos extremos de la conexión, pero no de la capa física intermedia (a menudo, un enlace satelital de baja calidad). Es por eso que esta verificación de tiempo de espera está vigente.
  2. Dado que los datos son asíncronos, hay una disposición para enviar un pequeño latido si no se han enviado datos desde el servidor durante un período de tiempo igual a la mitad del tiempo de espera.
  3. Puede haber varias instancias de este proceso (es decir, en la máquina que se conecta a varios servidores diferentes).
  4. Todas las comunicaciones utilizan métodos asíncronos (que supuestamente usan puertos de finalización).
  5. El proceso de verificación se ejecuta en un subproceso separado que es compartido por todos los clientes en una sola máquina.

Investigación completada (es decir, posibles soluciones)

Mis búsquedas de Google a este punto han conducido a lo siguiente:

  1. Me doy cuenta de que debería haber usado DateTime.UtcNow lugar de DateTime.Now por razones de rendimiento. Esto no afectaría el problema en sí.
  2. Una implementación que dependa de Ticks sería una mejor solución.
  3. Hay dos opciones para obtener ticks: Environment.TickCount y Stopwatch.GetTimestamp()
  4. Según mi investigación, Environment.TickCount puede ser susceptible de ajustes de tiempo, pero no estoy seguro en qué circunstancias. Además, dado que uso esta misma metodología en otras circunstancias de mayor rendimiento, la resolución de 10-16 mSeg puede ser un problema (aunque no en el caso específico que presento aquí).
  5. Stopwatch.GetTimestamp() puede retroceder a DateTime.Now.Ticks cuando un reloj de alto rendimiento no está disponible. No estoy seguro de la frecuencia con la que ocurrirá (las máquinas NO vienen con un reloj de alto rendimiento), pero estoy seguro de que, si recurren a Ticks, ocurrirá el mismo problema.
  6. También he leído que Stopwatch.GetTimestamp() usará la llamada a la API QueryPerformanceCounter() , y eso puede ser inestable cuando se llama desde varios subprocesos.

Última pregunta

Tengo curiosidad por saber cuál sería el mejor método para generar la lastRxTime tiempo lastRxTime ? ¿Me preocupo demasiado por los problemas de baja probabilidad en las funciones Environment.TickCount y Stopwatch.GetTimestamp() ? Estoy abierto a implementaciones alternativas siempre que tengan en cuenta la naturaleza de subprocesos múltiples de la aplicación, así como los problemas de calidad del enlace.

ACTUALIZACIÓN 17/07/2013 (¡Se ha implementado una solución!)

He implementado una solución y quiero que todos conozcan los detalles. En general, puede que no haya una respuesta aceptada, pero después de pasar por esta experiencia, puedo decir que la solución original fue definitivamente un problema. Trataré de proporcionar tantos detalles como sea posible:

Primero, el problema de NTP era en realidad un síntoma de un problema diferente. La red que presenta el problema es AD Domain con los dos servidores que ejecutan mi código configurado como Controladores de dominio. Resulta que los CD son fuentes de tiempo para el dominio. También resulta que la hora del sistema se desvía del reloj en tiempo real en estos sistemas hasta un minuto durante aproximadamente 11 días, momento en el que Windows está corrigiendo el deslizamiento. Una vez que corrige el deslizamiento en el primer DC, el segundo DC sincroniza su tiempo y ambos se encuentran con el problema descrito anteriormente.

Basándome en los comentarios y mi investigación original, creé un programa de prueba para ejecutarlo durante una desconexión para registrar valores para DateTime.Now, Environment.TickCount y Stopwatch.GetTimestamp (). Lo que encontré fue que durante la corrección, ni Environment.TickCount ni StopWatch.GetTimeStamp () se deslizaron, lo que significa que eran buenos candidatos para usar como reemplazo de DateTime.Now (). Fui con TickCount porque está garantizado para estar en todos mis servidores implementados (mientras que el cronómetro podría recurrir al objeto DateTime en algunas máquinas que aún no he encontrado). Está funcionando hasta ahora sin problema. Hice la debida diligencia en el problema de renovación para evitar que esa forma fuera un problema, pero tendré que esperar a que mi sistema esté funcionando durante tanto tiempo.

Quiero señalar que si alguien más está experimentando problemas similares, no debe descartar el uso de ninguna de las otras soluciones presentadas en la lista a continuación. Cada uno tiene su propio error, de hecho, el contador simple es probablemente la mejor solución para la mayoría de las circunstancias. La razón por la que no fui a esta solución fue que tengo un código similar en un área separada que depende en gran medida de los tiempos ajustados. Puedo manejar la resolución de 16 mSeg o más del conteo de tics allí, pero no puedo manejar la deriva del tiempo en que incurren las soluciones contrarias (usé un código como ese en un producto separado que terminó a la deriva por más de un segundo por hora y me trajo fuera de especificaciones para el proyecto).

Nuevamente, gracias a todos y si surge algo más, me aseguraré de actualizar la pregunta.



Related


Tags

c#   datetime