我设计了一个Timer
类,使用观察者模式每n
秒分派一个事件。当然,它会创建一个新线程,以避免阻塞调用它的线程。
然后我想:如果有100个客户端连接到我的服务器程序,我为每个客户端创建3个计时器,那么我将运行300个线程。这不是太多了吗?运行300个线程是否合适?
之后我在这里得知,在AS3中,Timer
在主线程中运行。我想知道:这是如何实现的?如何在主线程中运行计时器而不会阻塞它?在C++中是否可能实现?
我设计了一个Timer
类,使用观察者模式每n
秒分派一个事件。当然,它会创建一个新线程,以避免阻塞调用它的线程。
然后我想:如果有100个客户端连接到我的服务器程序,我为每个客户端创建3个计时器,那么我将运行300个线程。这不是太多了吗?运行300个线程是否合适?
之后我在这里得知,在AS3中,Timer
在主线程中运行。我想知道:这是如何实现的?如何在主线程中运行计时器而不会阻塞它?在C++中是否可能实现?
现在这是一个设计问题,所以每个人都有不同的意见,它也取决于您的要求,但在我看来,计时器不应该决定线程策略 - 客户端应该做到这一点。
我不确定您期望的行为是什么,但如果您在同一线程上运行300个计时器事件,并且一个事件处理程序由于某种原因被阻塞,其他事件处理程序将永远不会被触发。
一种可能性是在线程上创建计时器,但通过线程池以其他线程执行事件处理程序。当然,仍然有可能出现问题,因为如果您有许多长时间运行的处理程序,线程池可能会耗尽。
我强烈建议不要为每个处理程序使用显式的新线程,因为上下文切换很可能会影响性能。线程池在平衡这方面要好得多。
关于在主线程中实现计时器,必须有一些机制定期从用户代码中调用(例如,在事件轮询期间)并处理计时器。当然,这种方法很可能不准确,因为它只能在主线程的用户代码允许时执行计时器。
同时,当回调代码被执行时,它将阻塞主线程。
你的第一个问题已经有足够的答案了:使用线程池(一组5或10个线程)来处理计时器事件是通常的做法,也是在单个事件线程和所有事件线程之间取得平衡的好方法。
关于你的第二个问题:使用常规编程方式意味着你不能在主线程中执行计时器事件处理程序。如果你这样做,它会“阻塞”主线程,但这是不可能的,除非得到主线程中正在执行的代码的同意和支持。
主线程必须不时停止并检查是否有计时器事件,以对象形式获取计时器参数,然后处理事件。有许多设计原则可以实现这一点,但这是通常的做法。
在Unix系统上,你也可以考虑使用信号,但我认为这不是一个好主意。
您的服务器可以为所有计时器运行一个计时器线程。这个计时器轮
在客户端计时器注册到服务器计时器轮时创建事件。当注册的计时器超时时,计时器轮会设置事件。客户端获取在计时器注册时创建的事件句柄。客户端可以等待事件信号,表示已注册的计时器已超时。这样,线程的创建取决于客户端。
既然你在使用C++进行设计,你可以使用Boost ASIO计时器。我也基于它们设计了一个定时器类,它运行得很好,并且不需要任何线程-它使用异步调用到操作系统,因此你只需定义一个回调函数,在计时器到期时调用该函数,并调用计时器的async_wait函数,这是非阻塞的。当你声明计时器对象时,只需传递一个io_service对象,它是ASIO接口到操作系统。该对象负责服务您的异步请求和回调,因此您可以调用其阻塞方法run来完成此操作。在我的情况下,我不能让主线程阻塞,因此我只有一个线程,在其中这个唯一的调用是阻塞的。
在这里,你可以找到如何使用Boost ASIO异步计时器的示例:
http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/tutorial/tuttimer2.html
我的AbstractAsioTimer类被设计成可被子类化,因此onTimerTick方法将特定于派生类的结束。虽然您的需求可能有所不同,但这可能是一个很好的起点:
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();
}