microseconds c++ c++11 thread-safety chrono

microseconds - usleep c++ linux



¿Por qué no hay una alternativa segura para hilos de C++ 11 a std:: localtime y std:: gmtime? (5)

En C ++ 11, todavía tiene que usar std::localtime y std::gmtime como indirección para imprimir un std::chrono::time_point . Estas funciones no son seguras para usar en un entorno multiproceso como se introdujo con C ++ 11 porque devuelven un puntero a una estructura estática interna. Esto es especialmente molesto ya que C ++ 11 introdujo la conveniente función std::put_time que casi no se puede usar por el mismo motivo.

¿Por qué esto es tan fundamental roto o me pasa algo?


Como otros mencionaron, realmente no hay conveniencia de hilos y formato de formato de tiempo portátil en cualquier estándar de C ++ disponible, pero hay alguna técnica de preprocesador arcaica que encontré utilizable (gracias a Andrei Alexandrescu en CppCon 2015 diapositivas 17 y 18):

std::mutex gmtime_call_mutex; template< size_t For_Separating_Instantiations > std::tm const * utc_impl( std::chrono::system_clock::time_point const & tp ) { thread_local static std::tm tm = {}; std::time_t const time = std::chrono::system_clock::to_time_t( tp ); { std::unique_lock< std::mutex > ul( gmtime_call_mutex ); tm = *std::gmtime( &time ); } return &tm; } #ifdef __COUNTER__ #define utc( arg ) utc_impl<__COUNTER__>( (arg) ) #else #define utc( arg ) utc_impl<__LINE__>( (arg) ) #endif

Aquí declaramos la función con el argumento de la plantilla size_t y devolvemos el puntero al miembro estático std::tm . Ahora cada llamada de esta función con argumento de plantilla diferente crea una nueva función con la nueva variable estática std::tm . Si se define __COUNTER__ macro, se debe reemplazar por un valor entero incrementado cada vez que se usa, de lo contrario usaremos __LINE__ macro y en este caso mejor para asegurarnos de no llamar a macro utc dos veces en una línea.

Global gmtime_call_mutex protege la llamada std::gmtime en cada std::gmtime de instancias, y al menos en Linux no debería ser un problema de rendimiento ya que la adquisición de cerradura se realiza primero como spinlock, y en nuestro caso nunca debería terminar con un bloqueo de rosca real .

thread_local asegura que diferentes hilos que ejecutan el mismo código con llamadas utc seguirán funcionando con diferentes variables std::tm .

Ejemplo de uso:

void work_with_range( std::chrono::system_clock::time_point from = {} , std::chrono::system_clock::time_point to = {} ) { std::cout << "Will work with range from " << ( from == decltype(from)() ? std::put_time( nullptr, "beginning" ) : std::put_time( utc( from ), "%Y-%m-%d %H:%M:%S" ) ) << " to " << ( to == decltype(to)() ? std::put_time( nullptr, "end" ) : std::put_time( utc( to ), "%Y-%m-%d %H:%M:%S" ) ) << "." << std::endl; // ... }


De acuerdo con N2661 , el documento que agregó <chrono> :

Este documento no ofrece servicios calendáricos a excepción de un mapeo mínimo desde y hacia time_t de C.

Como este documento no propone una biblioteca de fecha / hora, ni especifica épocas, tampoco aborda los segundos intercalares. Sin embargo, una biblioteca de fecha / hora encontrará que esta es una excelente base sobre la cual construir.

Este documento no propone una biblioteca de cantidades físicas de propósito general.

Este documento propone una base sólida que, en el futuro, podría proporcionar un punto de partida compatible para una biblioteca de unidades físicas generales. Si bien dicha biblioteca futura podría tomar cualquiera de varias formas, la presente propuesta se detiene antes de ser realmente una biblioteca de unidades físicas. Esta propuesta es específica del tiempo y sigue estando motivada por las necesidades relacionadas con el tiempo de la biblioteca de subprocesos.

El objetivo principal de esta propuesta es satisfacer las necesidades de la API estándar de subprocesamiento de bibliotecas de una manera que sea fácil de usar, segura de usar, eficiente y lo suficientemente flexible como para no quedar obsoleta 10 o incluso 100 años a partir de ahora. Cada característica contenida en esta propuesta está aquí por una razón específica con casos de uso práctico como motivación. Las cosas que cayeron en la categoría de "cool", o "que suena como que podrían ser útiles", o "muy útiles pero no necesarias para esta interfaz" no se han incluido. Dichos artículos pueden aparecer en otras propuestas, y posiblemente se dirijan a un TR.

Tenga en cuenta que el objetivo principal de <chrono> es "satisfacer las necesidades de la API de threading de biblioteca estándar", que no requiere servicios de calendario.


No existe una alternativa segura para hilos a std::localtime y std::gmtime porque no propones una y la clasificas a lo largo de todo el proceso de estandarización. Y tampoco lo hizo nadie más.

El único código de calendario de chrono es un código que ajusta las funciones time_t existentes. Estandarizar o escribir nuevos estaba fuera del dominio del proyecto de chrono . Hacer tal estandarización requeriría más tiempo, más esfuerzo y agregará más dependencias. Simplemente envolviendo cada función time_t era simple, tenía pocas dependencias y era rápido.

Concentraron su esfuerzo por poco. Y tuvieron éxito en lo que se enfocaron.

Te animo a que comiences a trabajar en <calendar> o te unas a tal esfuerzo para crear una sólida API de calendario para std . Buena suerte y buena suerte!


Si está dispuesto a utilizar una biblioteca de terceros de fuente abierta gratuita, aquí hay una forma de imprimir std::chrono::system_clock::time_point en UTC:

#include "date.h" #include <iostream> int main() { using namespace date; using namespace std::chrono; std::cout << system_clock::now() << " UTC/n"; }

Esta es una alternativa segura para subprocesos a std::gmtime utilizando la sintaxis moderna de C ++.

Para una sustitución std::localtime moderna y segura para subprocesos, necesita esta biblioteca de zona horaria de nivel superior estrechamente relacionada y la sintaxis se ve así:

#include "tz.h" #include <iostream> int main() { using namespace date; using namespace std::chrono; std::cout << make_zoned(current_zone(), system_clock::now()) << "/n"; }

Ambos saldrán con la precisión que soporte su system_clock , por ejemplo:

2016-07-05 10:03:01.608080 EDT

(microsegundos en macOS)

Estas bibliotecas van mucho más allá de un reemplazo de gmtime y gmtime . Por ejemplo, ¿quieres ver la fecha actual en el calendario juliano?

#include "julian.h" #include <iostream> int main() { using namespace std::chrono; std::cout << julian::year_month_day(date::floor<date::days>(system_clock::now())) << "/n"; } 2016-06-22

¿Qué hay de la hora actual de GPS?

#include "tz.h" #include <iostream> int main() { using namespace date; std::cout << std::chrono::system_clock::now() << " UTC/n"; std::cout << gps_clock::now() << " GPS/n"; } 2016-07-05 14:13:02.138091 UTC 2016-07-05 14:13:19.138524 GPS

github.com/HowardHinnant/date

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html


gmtime y gmtime tienen un almacenamiento interno que es estático, lo que significa que no son seguros (tenemos que devolver un puntero a una estructura de datos, por lo que debe asignarse dinámicamente, un valor estático o un valor global, ya que la asignación dinámica memoria, esa no es una solución razonable, lo que significa que tiene que ser una variable global o estática [teóricamente, se podría asignar y almacenar en TLS, y hacerlo seguro para hilos de esa manera]).

La mayoría de los sistemas tienen alternativas seguras para hilos, pero no son parte de la biblioteca estándar. Por ejemplo, Linux / Posix tiene gmtime_r y gmtime_r , que toma un parámetro adicional para el resultado. Ver por ejemplo http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html

Del mismo modo, las bibliotecas de Microsoft tienen gmtime_s , que también es reentrante y funciona de manera similar (pasando el parámetro de salida como una entrada). Ver http://msdn.microsoft.com/en-us/library/3stkd9be.aspx

¿En cuanto a por qué la biblioteca estándar de C ++ 11 no utiliza estas funciones? Tendría que preguntarle a la gente que escribió esa especificación: espero que sea portátil y conveniente, pero no estoy del todo seguro.