example - clock c++
¿Se puede implementar un temporizador sin "dormir" con el estándar c++/c++ 11 solamente? (3)
C ++ 11 nos proporciona std::condition_variable
. En su temporizador puede esperar hasta que se cumpla su condición:
// Somewhere else, e.g. in a header:
std::mutex mutex;
bool condition_to_be_met{false};
std::condition_variable cv;
// In your timer:
// ...
std::unique_lock<std::mutex> lock{mutex};
if(!cv.wait_for(lock, std::chrono::milliseconds{timeout_ms}, [this]{return condition_to_be_met;}))
std::cout << "timed out!" << std::endl;
Puede encontrar más información aquí: https://en.cppreference.com/w/cpp/thread/condition_variable
Para señalar que la condición se ha cumplido, haga esto en otro hilo:
{
std::lock_guard<std::mutex> lock{mutex}; // Same instance as above!
condition_to_be_met = true;
}
cv.notify_one();
Tengo el siguiente código (copiado a mano en):
// Simple stop watch class basically takes "now" as the start time and
// returns the diff when asked for.
class stop_watch {...}
// global var
std::thread timer_thread;
void start_timer(int timeout_ms)
{
timer_thread = std::thread([timeout_ms, this](){
stop_watch sw;
while (sw.get_elapsed_time() < timeout_ms)
{
// Here is the sleep to stop from hammering a CPU
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// Do timeout things here...
std::cout << "timed out!" << std::endl;
})
}
No quería quedarme demasiado atascado en los detalles de la clase I que escribí, así que esta es una versión muy reducida. La clase completa llama a una función de devolución de llamada y tiene una variable para cancelar el temporizador, etc.
Solo quería enfocarme en la parte de "dormir". ¿Puedo implementar algo como esto sin dormir o hay una mejor manera de hacerlo? - ¿O es el sueño perfectamente bueno? - Consideré que los sueños son generalmente una señal de mal diseño (lo he leído en algunos lugares) ... pero no se me ocurre una manera de implementar un temporizador sin uno :(
Nota adicional: El temporizador debe tener el requisito de poder ser detenido / activado en cualquier momento. Solo agregue eso para mayor claridad porque parece afectar el tipo de solución a la que ir. En mi código original (no en este fragmento) utilicé un indicador bool atómico que puede salir del bucle.
Puede estar ocupado, esperar, verificando el tiempo en un bucle, hasta que alcance el tiempo que usted está esperando. Eso es obviamente horrible, así que no lo hagas. Dormir durante 10 ms es algo mejor, pero sin duda un diseño pobre. ( La respuesta de @ Damon tiene buena información ).
No hay nada de malo en usar funciones con sleep
en su nombre si eso es lo más útil que puede hacer su programa en ese momento.
La sugerencia de evitar el sleep
probablemente sea una recomendación en contra del patrón general de dormir por un corto tiempo, verificando si hay algo que hacer, y luego dormir nuevamente. En su lugar, bloquee la espera de un evento sin tiempo de espera o un tiempo de espera muy largo. (por ejemplo, una GUI debe esperar una pulsación de tecla / clic al llamar a una función de bloqueo, con un tiempo de espera configurado para activarlo cuando sea el momento de guardar automáticamente o lo que suceda en el futuro. Normalmente, no necesita un hilo separado solo para duerma, pero podría hacerlo si no hay ningún lugar sensato para insertar cheques para la hora actual.)
Dejar que el sistema operativo te despierte cuando finalmente es hora de hacer algo es mucho mejor. Esto evita los cambios de contexto y la contaminación de la memoria caché ralentizando otros programas y desperdiciando energía mientras se está girando en el modo de inactividad breve.
Si sabes que no hay nada que hacer por un tiempo, simplemente duerme por tanto tiempo con un solo sueño. AFAIK, los tiempos de inactividad múltiples no mejorarán la precisión del tiempo de activación en los sistemas operativos convencionales como Windows, Linux u OS X. Puede evitar un caché de instrucciones si su código se ha estado despertando con frecuencia, pero si esa cantidad de la demora es un problema real, probablemente necesite un sistema operativo en tiempo real y un enfoque mucho más sofisticado para la sincronización.
En todo caso, es más probable que un subproceso que haya estado durmiendo durante mucho tiempo se despierte exactamente cuando lo solicitó, mientras que un subproceso que se ejecutó recientemente y durmió solo 10 ms podría tener problemas con el intervalo de tiempo del programador. En Linux, los hilos que han estado dormidos durante un tiempo obtienen un impulso de prioridad cuando se despiertan.
Usar una función sin sleep
en el nombre que bloquea durante 1 segundo no es mejor que usar sleep
o this_thread::sleep_for
.
(Aparentemente, quieres que otro hilo te despierte. Este requisito está oculto en la pregunta, pero sí, una variable de condición es una buena forma portátil de hacerlo).
Si desea usar ISO C ++ 11 puro, entonces std::this_thread::sleep_for
o std::this_thread::sleep_until
son su mejor apuesta. Estos se definen en el encabezado estándar <thread>
.
sleep(3)
es una función POSIX (como nanosleep
), que no forma parte de ISO C ++ 11. Si eso no es un problema para usted, entonces siéntase libre de usarlo si es apropiado.
Para la suspensión de un intervalo asistido por sistema operativo de alta precisión, se introdujo C ++ 11
std::this_thread::sleep_for (La página cppreference tiene un código de ejemplo de su uso).
Bloquea la ejecución del subproceso actual para al menos la duración del sueño especificada.
Esta función puede bloquearse durante más tiempo que sleep_duration debido a retrasos en la programación o la contención de recursos.
La norma recomienda que se use un reloj estable para medir la duración. Si una implementación usa un reloj del sistema, el tiempo de espera también puede ser sensible a los ajustes del reloj.
Para dormir hasta que el reloj llegue a una hora específica (posiblemente teniendo en cuenta los cambios / correcciones a la hora del sistema):
Bloquea la ejecución del subproceso actual hasta que se haya alcanzado el tiempo de
sleep_time
especificado.Se utiliza el reloj vinculado a
sleep_time
, lo que significa que se tienen en cuenta los ajustes del reloj. Por lo tanto, la duración del bloque puede, pero no, ser menor o mayor quesleep_time - Clock::now()
en el momento de la llamada, dependiendo de la dirección del ajuste. La función también puede bloquearse durante más tiempo hasta que sesleep_time
tiempo desleep_time
debido a retrasos en la programación o la contención de recursos.
Tenga en cuenta que sleep_for
no debe verse afectado por los cambios en el reloj del sistema, por lo que duerme durante mucho tiempo real.
Pero se supone que sleep_until
le permitirá despertarse cuando el reloj del sistema llegue a una hora determinada, incluso si lo hizo al ser ajustado (NTP o configuración manual), si se usa con un reloj que no sea steady_clock
.
Consecuencias del sueño: despertar tarde / temprano
Las advertencias sobre la posibilidad de dormir demasiado tiempo también se aplican al sleep
y al sleep
nanosleep
, o cualquier otro sueño o tiempo de espera específico del sistema operativo (incluido el enfoque de condición-variable en la respuesta de @ Sebastian), por supuesto. Es inevitable; sin embargo, un sistema operativo en tiempo real puede darte límites superiores en ese retraso adicional.
Ya estás haciendo algo como esto con tus 10ms de sueño:
Siempre asuma que el sleep
o cualquier otra función se despertó tarde, y verifique la hora actual en lugar de utilizar la cuenta muerta en cualquier caso que sea importante.
No se puede construir un reloj confiable fuera del sleep
repetido. por ejemplo, no genere un temporizador de cuenta regresiva que duerme durante 1 segundo, disminuye y muestra un contador, luego duerme durante otro segundo. A menos que sea solo un juguete y no te importe mucho la precisión.
Algunas funciones de suspensión, como la sleep(3)
POSIX sleep(3)
también pueden activarse temprano con una señal. Si despertarse demasiado pronto es un problema de corrección, verifique el tiempo y, si es necesario, vuelva a dormir para el intervalo calculado. (O use sleep_until
)
Si bien su código "funcionará", es subóptimo para el propósito previsto como temporizador.
Existe std::this_thread::sleep_until
que, dependiendo de la implementación, posiblemente solo llame a sleep_for
después de hacer algunos cálculos matemáticos, pero podría usar un temporizador adecuado, que es muy superior en términos de precisión y confiabilidad.
En general, dormir no es lo mejor, lo más confiable y lo más preciso que se puede hacer, pero a veces, si lo que se pretende es esperar un tiempo aproximado, puede ser "lo suficientemente bueno".
En cualquier caso, dormir mal por pequeñas cantidades como en tu ejemplo es una mala idea. Esto quemará una gran cantidad de CPU en subprocesos innecesarios de reprogramación y activación, y en algunos sistemas (Windows en particular, aunque Windows 10 ya no es tan malo a este respecto) puede agregar una cantidad considerable de fluctuación e incertidumbre. Tenga en cuenta que las diferentes versiones de Windows se ajustan a la granularidad del programador de manera diferente , por lo que además de no ser demasiado precisos, ni siquiera tiene un comportamiento consistente. El redondeo es prácticamente "a quién le importa" una sola espera grande, pero es un problema serio para una serie de esperas pequeñas.
A menos que la capacidad de abortar el temporizador prematuramente sea una necesidad (¡pero en ese caso, también hay mejores formas de implementar eso!), Debe dormir exactamente una vez , nunca más, durante toda la duración. Para asegurarse de que sea correcto, debe verificar que efectivamente obtuvo el tiempo que esperaba porque algunos sistemas (en particular, POSIX) pueden estar en reposo.
Quedarse dormido es un problema diferente si lo necesita correctamente , porque incluso si controla y detecta ese caso correctamente, una vez que sucede, no hay nada que pueda hacer al respecto (el tiempo ya pasó y nunca vuelve). Pero, ay, eso es solo una debilidad fundamental de dormir, no hay mucho que puedas hacer. Afortunadamente, la mayoría de las personas pueden ignorar este problema la mayor parte del tiempo.