C ++`Timer`类实现

时间:2012-09-13 08:26:22

标签: c++ multithreading algorithm class timer

我设计了一个Timer类,每隔n n秒调度一次事件(使用Observer模式)。当然它会创建一个新线程,以便不阻止调用它的线程。

然后我想 - 嗯......让我们说100个客户端连接到我的服务器程序,我为每个客户端创建了3个计时器,所以我运行了300个线程。不是很多吗?它是ok,我运行300个线程吗?

然后我{3}在AS3中Timer在主线程中运行。我想知道:怎么样?如何实现在主线程中运行的计时器而不是阻塞它?在C ++中可以吗?

7 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:2)

您可以创建一个单一的计时器线程,并为每个"注册"的客户端创建一个树中的条目。密钥是客户端超时,值将是对客户端的引用。这会按照超时的顺序对客户进行排序。

然后对于计时器,设置一个循环计时器,比如说每100毫秒(相应调整)。当计时器到期时,迭代树删除并调度已超时的每个客户端。当您达到尚未超时的客户端超时时,迭代应该停止。

这种方法的更准确的改进是当计时器到期,并且调度客户端,计算下一个客户端的超时并相应地设置计时器。这只取决于解决方案的准确程度。

答案 2 :(得分:1)

现在这是一个设计问题,所以每个人都有不同的意见,这也取决于你的要求,但IMO,计时器不应该决定线程政策本身 - 客户应该这样做。

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

一种可能性是在线程上创建一个计时器,但是以一种通过线程池在其他线程上执行事件处理程序的方式实现它。当然,仍有可能破坏事物,因为如果你有许多长时间运行的处理程序,线程池可能会耗尽。

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

答案 3 :(得分:1)

就在主线程中实现定时器而言,必须存在一些从用户代码周期性调用的机制(例如,在事件轮询期间),该机制也处理定时器。当然,这种方法很可能是不准确的,因为它只能在主线程中的用户代码允许的情况下执行定时器。

当然,它会在执行回调代码时阻塞主线程。

答案 4 :(得分:1)

您的第一个问题已经有了足够的答案:处理计时器事件的线程池(一组说5或10个线程)是通常的方法,并且每个事件的一个线程与一个线程之间的良好折衷适用于所有活动。

关于你的第二个问题:使用常规编程意味着你不能在主线程中执行timer事件处理程序。如果可以,它会阻止"阻止"主线程,但如果没有得到主线程中执行的代码的同意和支持,这是不可能的。

主线程必须不时停止并检查定时器是否有事件,以对象的形式从定时器中获取参数,然后处理事件。有很多方法可以设计这个原则,但这就是你做这件事的一般方法。

在Unix系统上,您可能还会考虑使用信号,但我认为这不是一个好主意。

答案 5 :(得分:1)

您的服务器可能会为所有计时器运行一个计时器线程。当客户机计时器注册到服务器计时器轮时,此timer wheel会创建事件。当注册的计时器超时时,事件由计时器轮设置。客户端获取定时器注册时创建的事件的句柄。客户端可以等待发出注册的定时器超时的事件。这样,线程创建取决于客户端。

答案 6 :(得分:0)

由于您使用C ++进行设计,因此可以使用Boost ASIO计时器。我还设计了一个基于它们的Timer类,它运行良好,没有任何线程 - 它使用异步调用操作系统,所以基本上你只需定义一个回调,当定时器到期时调用它然后调用定时器&# 39; s async_wait 函数,它是非阻塞的。当你声明你的计时器对象时,你只需要传递一个 io_service 对象,该对象是O.S的ASIO接口。此对象负责为异步请求和回调提供服务,因此您可以将其称为阻止方法运行。在我的情况下,我无法阻止主线程,所以我只有一个线程,其中这个唯一的调用阻塞。

在这里您可以找到有关如何使用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();
}