C++计时器类的实现

14

我设计了一个Timer类,使用观察者模式每n秒分派一个事件。当然,它会创建一个新线程,以避免阻塞调用它的线程。

然后我想:如果有100个客户端连接到我的服务器程序,我为每个客户端创建3个计时器,那么我将运行300个线程。这不是太多了吗?运行300个线程是否合适?

之后我在这里得知,在AS3中,Timer在主线程中运行。我想知道:这是如何实现的?如何在主线程中运行计时器而不会阻塞它?在C++中是否可能实现?


大多数(或至少许多)计时器实现实际上是阻塞的。这并没有本质上的问题。显然,这取决于目的。 - Konrad Rudolph
7个回答

9
一个可能的解决方案是只使用一个线程来处理所有计时器,并按超时时间排序排列队列。但问题在于,当计时器到期并调用回调函数时,它将在全局计时器线程的上下文中运行而不是单独运行。这可以通过为事件生成一个新线程并直接加入,或者通过使用线程池来处理事件,以便主计时器线程不会“被堵塞”来解决。

3
您可以创建一个单一的计时器线程,并为每个“注册”的客户端在树中创建一个条目。 键将是客户端超时,值将是对客户端的引用。 这将按其超时时间对客户进行排序。
然后,设置一个周期性定时器,比如每100毫秒(请适当调整)。 当计时器到期时,迭代树并删除和分派已超时的每个客户端。 迭代应在达到尚未超时的客户端超时时停止。
这种方法的更准确的改进是,当计时器到期并且客户端被分派时,计算下一个客户端的超时时间并相应地设置计时器。 它取决于解决方案需要多么准确。

1

现在这是一个设计问题,所以每个人都有不同的意见,它也取决于您的要求,但在我看来,计时器不应该决定线程策略 - 客户端应该做到这一点。

我不确定您期望的行为是什么,但如果您在同一线程上运行300个计时器事件,并且一个事件处理程序由于某种原因被阻塞,其他事件处理程序将永远不会被触发。

一种可能性是在线程上创建计时器,但通过线程池以其他线程执行事件处理程序。当然,仍然有可能出现问题,因为如果您有许多长时间运行的处理程序,线程池可能会耗尽。

我强烈建议不要为每个处理程序使用显式的新线程,因为上下文切换很可能会影响性能。线程池在平衡这方面要好得多。


1

关于在主线程中实现计时器,必须有一些机制定期从用户代码中调用(例如,在事件轮询期间)并处理计时器。当然,这种方法很可能不准确,因为它只能在主线程的用户代码允许时执行计时器。

同时,当回调代码被执行时,它将阻塞主线程。


1

你的第一个问题已经有足够的答案了:使用线程池(一组5或10个线程)来处理计时器事件是通常的做法,也是在单个事件线程和所有事件线程之间取得平衡的好方法。

关于你的第二个问题:使用常规编程方式意味着你不能在主线程中执行计时器事件处理程序。如果你这样做,它会“阻塞”主线程,但这是不可能的,除非得到主线程中正在执行的代码的同意和支持。

主线程必须不时停止并检查是否有计时器事件,以对象形式获取计时器参数,然后处理事件。有许多设计原则可以实现这一点,但这是通常的做法。

在Unix系统上,你也可以考虑使用信号,但我认为这不是一个好主意。


1

您的服务器可以为所有计时器运行一个计时器线程。这个计时器轮在客户端计时器注册到服务器计时器轮时创建事件。当注册的计时器超时时,计时器轮会设置事件。客户端获取在计时器注册时创建的事件句柄。客户端可以等待事件信号,表示已注册的计时器已超时。这样,线程的创建取决于客户端。


0

既然你在使用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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接