C ++`Timer`类实现

问题描述 投票:13回答:7

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

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

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

c++ multithreading algorithm class timer
7个回答
7
投票

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


2
投票

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

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

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


1
投票

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

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

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

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


1
投票

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

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


1
投票

你的第一个问题已经有了足够的答案:处理计时器事件的线程池(一组5或10个线程)通常是这样做的,并且每个事件的一个线程和所有事件的一个线程之间有一个很好的折衷方案。 。

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

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

在Unix系统上你也可能会想到使用信号,但我认为这不是一个好主意。


1
投票

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


0
投票

由于您使用C ++进行设计,因此可以使用Boost ASIO计时器。我还设计了一个基于它们的Timer类,它运行良好,没有任何线程 - 它使用异步调用操作系统,所以基本上你只需要定义一个回调,当定时器到期时调用它,然后调用定时器的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();
}
© www.soinside.com 2019 - 2024. All rights reserved.