[英]C++ `Timer` class implementation
I've designed a Timer
class, which dispatches (using an Observer pattern) an event each n
n-seconds. 我设计了一个
Timer
类,它每n
n秒调度一次事件(使用Observer模式)。 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. 然后我想 - 嗯......让我们说100个客户端连接到我的服务器程序,我为每个客户端创建了3个计时器,所以我运行了300个线程。 Isn't it much?
不是很多吗? Is it an
ok
, that I run 300 threads? 它是
ok
,我运行300个线程?
Then I was told that in AS3 Timer
runs in main thread. 然后我被告知 AS3
Timer
在主线程中运行。 And I wondered: HOW??? 我想知道:怎么样? How can I implement a timer running in main thread and not blocking it?
如何实现在主线程中运行的计时器而不是阻塞它? Is it possible in C++?
在C ++中有可能吗?
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".
这个问题是当一个计时器到期并且你调用回调函数时,它将在全局计时器线程的上下文中运行而不是单独运行。这当然可以通过为事件生成一个新线程来解决,这是然后直接加入,或者通过一个线程池来处理事件,因此主计时器线程不会“堵塞”。
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). 然后对于计时器,设置循环计时器,比如说每100毫秒(相应地调整)。 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.
这取决于解决方案需要的准确程度。
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. 现在这是一个设计问题,所以每个人都有不同的意见,这也取决于你的要求,但IMO,计时器不应该决定线程策略本身 - 客户端应该这样做。
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. 我不确定您期望的行为,但如果您在同一个线程上的计时器上运行300个事件,并且由于某种原因在一个事件处理程序块上运行,则永远不会触发其他事件处理程序。
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.
线程池在平衡这个方面要好得多。
As far as implementing a timer in the main thread goes, there has to be some mechanism that is periodically called from user code (eg, 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. 当然,它也会在执行回调代码时阻塞主线程。
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. 你的第一个问题已经有了足够的答案:一个处理计时器事件的线程池(一组5或10个线程)通常是这样做的,并且每个事件的一个线程和所有事件的一个线程之间有一个很好的折衷方案。 。
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. 在Unix系统上你也可能会想到使用信号,但我认为这不是一个好主意。
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. 当客户机计时器注册到服务器计时器轮时,此
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.
这样,线程创建取决于客户端。
Since you are designing in C++ you can use Boost ASIO timers for that. 由于您使用C ++进行设计,因此可以使用Boost ASIO定时器。 I have also designed a Timer class based on them and it works nicely and without any threads - it uses asynchronous calls to the OS, 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.
我还设计了一个基于它们的Timer类,它运行良好,没有任何线程 - 它使用异步调用操作系统,所以基本上你只需要定义一个回调,当定时器到期时调用它,然后调用定时器的async_wait功能,非阻塞。 When you declare your timer object you just have to pass it an io_service object which is the ASIO interface to the OS This object is responsible for servicing your async requests and callbacks, so do to that you can call its blocking method run .
当你声明你的计时器对象时,你只需要传递一个io_service对象,它是OS的ASIO接口。这个对象负责处理异步请求和回调,所以你可以调用它的阻塞方法运行 。 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: 在这里,您可以找到有关如何使用Boost ASIO异步计时器的示例:
http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/tutorial/tuttimer2.html 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. 我的AbstractAsioTimer类被设计为子类,因此onTimerTick方法将特定于派生类的末尾。 Although your needs might be a little different, it might be a good starting point:
虽然您的需求可能会略有不同,但它可能是一个很好的起点:
abstractasiotimer.hpp: 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: 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();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.