【问题标题】:C++ `Timer` class implementationC++ `Timer` 类实现
【发布时间】:2018-12-01 22:44:18
【问题描述】:

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

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

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

【问题讨论】:

  • 大多数(或至少很多)定时器实现实际上是阻塞的。这本身并没有什么错。这显然取决于目的。

标签: c++ multithreading algorithm class timer


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    对这种方法的更准确的改进是当计时器到期并分派客户端时,计算下一个客户端的超时时间并相应地设置计时器。这仅取决于解决方案需要有多准确。

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

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

          主线程会时不时停下来检查定时器是否有事件,以对象的形式从定时器中获取参数,然后处理事件。有很多方法可以设计此原则,但这是您的一般做法。

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              【解决方案7】:

              由于您使用 C++ 进行设计,因此您可以使用 Boost ASIO 计时器。我还设计了一个基于它们的 Timer 类,它工作得很好,没有任何线程 - 它使用对操作系统的异步调用,所以基本上你只需要定义一个回调,它将在定时器到期时调用,然后调用定时器的 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();
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-05-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多