2012-04-12 11 views
7

Cuando intento obtener (no establecido) el tiempo de caducidad actual usando boost expires_from_now() parece cancelar realmente el temporizador, pero en realidad funciona como se esperaba, pero finalmente no llama al controlador.¿Por qué la función impulso asio expires_from_now() cancelar un deadline_timer?

O, en otras palabras, al acceder a un deadline_timer con expires_from_now() llamará al controlador inmediatamente y no llamará al controlador cuando caduque.

Por favor, considere el siguiente código y la correspondiente salida:

#include <boost/asio.hpp> 
#include <boost/thread.hpp> 
#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/date_time/posix_time/posix_time_io.hpp> 
#include <iostream> 

using namespace boost::posix_time; 
using namespace std; 

void handler1(const boost::system::error_code &ec) 
{ 
    if (ec == boost::asio::error::operation_aborted) 
    { 
     std::cout << microsec_clock::local_time() << " Handler1: Timer 1 was cancelled or retriggered." << std::endl; 
    } 
    else 
    { 
     std::cout << microsec_clock::local_time() << " Handler1: expired." << std::endl; 
    } 
} 

boost::asio::io_service io_service1; 

class Mytimer { 
public: 
    Mytimer(); 
    void startTimer(); 
    void runTimerThread(); 
    bool isRunning(); 
private: 
    bool m_isRunning; 
    boost::asio::deadline_timer* m_pTimer; 
    boost::thread* m_pThread; 
}; 

Mytimer::Mytimer() 
    : m_pTimer(NULL), 
    m_pThread(NULL) 
{ 
    m_pTimer = new boost::asio::deadline_timer(io_service1); 
    m_pTimer->async_wait(handler1); 
} 

void Mytimer::runTimerThread() 
{ 
    io_service1.run(); 
} 

void Mytimer::startTimer() 
{ 
    m_pThread = new boost::thread(&Mytimer::runTimerThread, this); 
    m_pTimer->expires_from_now(boost::posix_time::seconds(10)); 
} 

bool Mytimer::isRunning() 
{ 

    time_duration td = m_pTimer->expires_from_now(); 
    if (td.total_seconds() > 0) 
    { 
     return true; 
    } 
    return false; 
} 

int main() 
{ 
    time_facet *facet = new time_facet("%Y%m%d %H:%M:%S%f"); 
    std::cout.imbue(std::locale(std::cout.getloc(), facet)); 

    cout << microsec_clock::local_time() << " before timer construction" << endl; 
    Mytimer timer; 
    cout << microsec_clock::local_time() << " before startTimer()" << endl; 
    timer.startTimer(); 
    cout << microsec_clock::local_time() << " IsRunning: " << timer.isRunning() << endl; 
    for (int i = 0; i <= 20; i++) 
    { 
     sleep(1); 
     cout << microsec_clock::local_time() << " IsRunning: " << timer.isRunning() << endl; 
    } 
} 

20120412 22: 41: 45689235 antes de la construcción del temporizador
20120412 22: 41: 45689514 antes startTimer()
20120412 22:41: 45689619 IsRunning: 1
20120412 22: 41: 45689693 Handler1: temporizador 1 fue cancelada o reactivar.
20120412 22: 41: 46689792 IsRunning: 1
20120412 22: 41: 47689994 IsRunning: 1
20120412 22: 41: 48690179 IsRunning: 1
20120412 22: 41: 49690375 IsRunning: 1
20120412 22:41 : 50690530 IsRunning: 1
20120412 22: 41: 51690712 IsRunning: 1
20120412 22: 41: 52690884 IsRunning: 1
20120412 22: 41: 53691069 IsRunning: 1
20120412 22: 41: 54691236 IsRunning: 0
20120412 22: 41: 55691428 IsRunning: 0
20120412 22: 41: 56691614 IsRunning: 0
20120412 22: 41: 57691810 IsRunning: 0
20120412 22: 41: 58692001 IsRunning: 0
20120412 22: 41: 59692193 IsRunning: 0
20120412 22:42 : 00692364 IsRunning: 0
20120412 22: 42: 01692542 IsRunning: 0
20120412 22: 42: 02692706 IsRunning: 0
20120412 22: 42: 03692886 IsRunning: 0
20120412 22: 42: 04693071 IsRunning: 0
20120412 22: 42: 05693267 IsRunning: 0
20120412 22: 42: 06693465 IsRunning: 0

+0

Creo que hay dos problemas aquí: Primero, en el constructor se llama al temporizador sin ninguna duración de caducidad, por lo que caducará inmediatamente. En segundo lugar, cuando la función miembro reinicia el temporizador, no hay controlador. Así que expires_from_now aún funciona (como se puede ver el 1 cambia a 0 después de 10 segundos), pero nunca se llamará al controlador, porque no hay ninguno asociado. –

+0

Mantendré la pregunta abierta por ahora, tal vez alguien pueda confirmar mis conclusiones. –

+1

+1 para el reproductor –

Respuesta

10

Cuando intento de conseguir (! No establecido) el tiempo de caducidad actual utilizando impulso expires_from_now() es parece que cancelar en realidad el contador de tiempo, sin embargo, realidad se ejecuta como se esperaba, pero finalmente no llama al controlador.

esta suposición es incorrecta. Al crear su deadline_timer

m_pTimer = new boost::asio::deadline_timer(io_service1); 
m_pTimer->async_wait(handler1); 

le has dicho que expire inmediatamente.Entonces, cuando se inicia el io_service

m_pThread = new boost::thread(&Mytimer::runTimerThread, this); 

se invoca posteriormente

m_pTimer->expires_from_now(boost::posix_time::seconds(10)); 

que, como la documentación describes clearly, esta se cancelará cualquier controlador pendientes, énfasis añadido es mío

Esta función establece el tiempo de vencimiento Cualquier espera asíncrona pendiente operaciones se cancelarán. El controlador para cada operación cancelada se invocará con el código de error boost::asio::error::operation_aborted .

Para resolver esto, cambiar su método Mytimer::startTimer() de la siguiente manera

--- timer4.cc.orig 2012-04-13 11:18:47.000000000 -0500 
+++ timer4.cc 2012-04-13 11:16:54.000000000 -0500 
@@ -1,4 +1,3 @@ 
- 
#include <boost/asio.hpp> 
#include <boost/thread.hpp> 
#include <boost/date_time/posix_time/posix_time.hpp> 
@@ -39,7 +38,6 @@ 
    m_pThread(NULL) 
{ 
    m_pTimer = new boost::asio::deadline_timer(io_service1); 
- m_pTimer->async_wait(handler1); 
} 

void Mytimer::runTimerThread() 
@@ -49,8 +47,9 @@ 

void Mytimer::startTimer() 
{ 
- m_pThread = new boost::thread(&Mytimer::runTimerThread, this); 
    m_pTimer->expires_from_now(boost::posix_time::seconds(10)); 
+ m_pTimer->async_wait(handler1); 
+ m_pThread = new boost::thread(&Mytimer::runTimerThread, this); 
} 

bool Mytimer::isRunning() 

que se traduce en el comportamiento esperado

mbp:stackoverflow samm$ ./a.out 
20120413 11:17:22922529 before timer construction 
20120413 11:17:22923155 before startTimer() 
20120413 11:17:22923530 IsRunning: 1 
20120413 11:17:23924702 IsRunning: 1 
20120413 11:17:24925971 IsRunning: 1 
20120413 11:17:25927320 IsRunning: 1 
20120413 11:17:26928715 IsRunning: 1 
20120413 11:17:27929969 IsRunning: 1 
20120413 11:17:28930601 IsRunning: 1 
20120413 11:17:29931843 IsRunning: 1 
20120413 11:17:30933098 IsRunning: 1 
20120413 11:17:31934366 IsRunning: 0 
20120413 11:17:32923594 Handler1: expired. 
20120413 11:17:32934692 IsRunning: 0 
20120413 11:17:33935922 IsRunning: 0 
20120413 11:17:34936638 IsRunning: 0 
20120413 11:17:35937905 IsRunning: 0 
20120413 11:17:36939133 IsRunning: 0 
20120413 11:17:37940407 IsRunning: 0 
20120413 11:17:38941043 IsRunning: 0 
20120413 11:17:39942319 IsRunning: 0 
20120413 11:17:40942662 IsRunning: 0 
20120413 11:17:41943989 IsRunning: 0 
20120413 11:17:42945284 IsRunning: 0 
20120413 11:17:43946555 IsRunning: 0 
mbp:stackoverflow samm$ 
+0

Gracias por su explicación detallada. –

+0

Tu salida abre otra pregunta. ¿Cómo diseño una función de miembro IsRunning() que no devuelve 0 mientras el temporizador todavía se está ejecutando? Una cosa que me viene a la mente es usar un indicador privado booleano dentro del controlador que se establece en falso en la función de controlador de miembros. Cambiar los total_seconds por total_nanoseconds podría ser otra opción. –

+0

@Michael no es obvio para mí lo que espera lograr con ese enfoque. Un 'deadline_timer' caducará después del tiempo otorgado o se cancelará. En ese caso, la solución es verificar el error dado al controlador de finalización. –

1

El siguiente código tiene ahora la inicialización deadline_timer correcta, y tiene una indicador bool privado, que permite determinar, si el temporizador se está ejecutando actualmente. También tiene un controlador local miembro.

class Mytimer { 
public: 
    Mytimer(); 
    void startTimer(int timeDelaySecs); 
    void cancelTimer(); 
    void runTimerThread(); 
    bool isRunning(); 
    void timerHandler(const boost::system::error_code& ec); 
private: 
    bool m_isRunning; 
    boost::asio::deadline_timer* m_pTimer; 
    boost::thread* m_pThread; 
}; 

Mytimer::Mytimer() 
    : m_pTimer(NULL), 
     m_pThread(NULL) 
{ 
    m_pTimer = new boost::asio::deadline_timer(io_service1); 
} 

void Mytimer::runTimerThread() 
{ 
    io_service1.run(); 
} 

void Mytimer::startTimer(int timeDelaySecs) 
{ 
    m_pTimer->expires_from_now(boost::posix_time::seconds(timeDelaySecs)); 
    m_pTimer->async_wait(boost::bind(&Mytimer::timerHandler, this, _1)); 
    m_pThread = new boost::thread(&Mytimer::runTimerThread, this); 
    m_isRunning = true; 
} 

bool Mytimer::isRunning() 
{ 
    return m_isRunning; 
} 

void Mytimer::timerHandler(const boost::system::error_code& ec) 
{ 
    if (ec == boost::asio::error::operation_aborted) 
    { 
     std::cout << microsec_clock::local_time() << " Handler1: Timer 1 was cancelled or retriggered." << std::endl; 
    } 
    else 
    { 
     std::cout << microsec_clock::local_time() << " Handler1: expired." << std::endl; 
    } 
    m_isRunning = false; 
} 
Cuestiones relacionadas