Reputation: 6230
I've designed a Timer
class, which dispatches (using an Observer pattern) an event each n
n-seconds. Of course it creates a new thread in order not to block the thread it was called from.
Then I've thought - hmmm... let's say 100 clients connect to my server-program, I create 3 timers for each of them, so I run 300 threads. Isn't it much? Is it an ok
, that I run 300 threads?
Then I was told that in AS3 Timer
runs in main thread. And I wondered: HOW??? How can I implement a timer running in main thread and not blocking it? Is it possible in C++?
Upvotes: 14
Views: 6640
Reputation: 2217
Since you are designing in C++ you can use Boost ASIO timers for that. I have also designed a Timer class based on them and it works nicely and without any threads - it uses asynchronous calls to the O.S., so basically you just have to define a callback which will be called when the timer expires and then call the timer's async_wait function, which is non-blocking. When you declare your timer object you just have to pass it an io_service object which is the ASIO interface to the O.S. This object is responsible for servicing your async requests and callbacks, so do to that you can call its blocking method run. In my case I couldn't have the main thread blocking, so I just had one thread in which this unique call was blocking.
Here you can find examples on how to use the Boost ASIO async timer:
http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/tutorial/tuttimer2.html
My AbstractAsioTimer class was designed to be subclassed so the onTimerTick method would be specific to the derived class ends. Although your needs might be a little different, it might be a good starting point:
abstractasiotimer.hpp:
#ifndef _ABSTRACTASIOTIMER_HPP_
#define _ABSTRACTASIOTIMER_HPP_
#include <boost/asio.hpp>
/**
* Encapsulates a POSIX timer with microsecond resolution
*/
class AbstractAsioTimer
{
public:
/**
* Instantiates timer with the desired period
* @param io ASIO interface object to the SO
* @param timeout time in microseconds for the timer handler to be executed
*/
AbstractAsioTimer(boost::asio::io_service& io, unsigned int timeout);
/**
* Destructor
*/
virtual ~AbstractAsioTimer();
/**
* Starts timer operation
*/
void timerStart();
/**
* Stops timer operation
*/
void timerStop();
/**
* Returns timer operation state
*/
bool isRunning() const;
/**
* Returns a reference to the underlying io_service
*/
boost::asio::io_service& get_io_service();
protected:
/**
* Timer handler to execute user specific code
* @note must be reimplemented in derived classes
*/
virtual void onTimerTick() = 0;
private:
/**
* Callback to be executed on timer expiration. It is responsible
* for calling the 'onTimerTick' method and restart the timer if
* it remains active
*/
void timerExpired(const boost::system::error_code& error);
boost::asio::deadline_timer timer; /**< ASIO timer object */
unsigned int timeout; /**< Timer period in microseconds */
bool running; /**< Flag to indicate whether the timer is active */
};
#endif
abstractasiotimer.cpp:
#include <iostream>
#include <boost/bind.hpp>
#include <boost/concept_check.hpp>
#include "abstractasiotimer.hpp"
using namespace boost::asio;
AbstractAsioTimer::AbstractAsioTimer(boost::asio::io_service& io,
unsigned int timeout):
timer(io), timeout(timeout),
running(false)
{
}
AbstractAsioTimer::~AbstractAsioTimer()
{
running = false;
timer.cancel();
}
void AbstractAsioTimer::timerExpired(const boost::system::error_code& error) {
if (!error) {
onTimerTick();
//Restart timer
timerStart();
}
else {
running = false;
std::cerr << "Timer stopped: " << error.message() << std::endl;
}
}
void AbstractAsioTimer::timerStart()
{
timer.expires_from_now(boost::posix_time::microseconds(timeout));
timer.async_wait(boost::bind(&AbstractAsioTimer::timerExpired,
this, placeholders::error));
running = true;
}
void AbstractAsioTimer::timerStop() {
running = false;
timer.cancel();
}
bool AbstractAsioTimer::isRunning() const {
return running;
}
io_service& AbstractAsioTimer::get_io_service()
{
return timer.get_io_service();
}
Upvotes: 0
Reputation: 5194
Your server may run one timer thread for all timers. This timer wheel
creates events when the clients timers are registered to the servers timer wheel. When the registered timer times out, the event is set by the timer wheel. The clients obtain the handle to the event created at the time the timer was registered. Clients can wait for the events signalling the registered timer timed out. This way the thread creation is up to the clients.
Upvotes: 1
Reputation: 4515
Your first question has already enough answers: A thread-pool (a set of say 5 or 10 threads) that handle the timer events is the usual way to do that and a good compromise between one thread for each event and one thread for all events.
Regarding your second question: Using regular programming means you cannot execute the timer event handler in the main thread. If you could it would "block" the main thread but that is not possible without consent and support from the code executing in the main thread.
The main thread would have to stop from time to time and check whether there is an event from the timer, take the parameters from the timer in the form of an object and than handle the event. There are many ways to design this principle but that is the general ways how you do it.
On Unix systems you might also think of using signals, but I believe that is no good idea.
Upvotes: 1
Reputation: 10357
You could create one single timer thread, and for each client that "registers", create an entry in a tree. The key would be the client timeout and the value would be a reference to the client. This would order the clients by their timeout.
Then for the timer, set a cyclic timer, say every 100 milliseconds (tune accordingly). When the timer expires, iterate the tree removing and dispatching each client that has timed out. The iteration should stop when you reach a client timeout that hasnt timed out yet.
A more accurate improvement to this approach would be when the timer expires, and the clients are dispatched, calculate the timeout of the next client and set the timer accordingly. It just depends on how accurate the solution needs to be.
Upvotes: 3
Reputation: 88
As far as implementing a timer in the main thread goes, there has to be some mechanism that is periodically called from user code (e.g., during event polling) that also handles the timers. Of course, such an approach is most likely inaccurate, because it can only execute timers when the user code in the main thread allows it.
Also it will block the main thread while the callback code is executed, of course.
Upvotes: 1
Reputation: 14581
Now this is a design question, so everyone has different opinions and it also depends on your requirements, but IMO, the timer should not decide threading policy itself - the client should do that.
I am not sure what behavior you expect, but if you run 300 events on a timer on the same thread and one event handler blocks for some reason, other event handlers will never be triggered.
One possibility is to create a timer on a thread, but implement it in a way that event handlers are executed on other threads via thread pool. Of course, it is still possible to break things, because if you have many long running handlers, thread pool might get exhausted.
I strongly suggest not using explicit new thread for each handler, as context switching would likely kill the performance. Thread pool is much better at balancing this.
Upvotes: 1
Reputation: 409136
A possible solution is to just use one thread for all timers, and have a queue ordered by the timeout. The problem with this is that when a timer expires and you call the callback function, it will run in the context of the global timer thread and not separately.This can of course be solved by spawning a new thread just for the event, which is then joined directly, or by having a thread-pool to handle the events, so the main timer thread will not be "clogged up".
Upvotes: 9