14

n秒Timerごとにイベントを(オブザーバーパターンを使用して)ディスパッチするクラスを設計しました。nもちろん、呼び出されたスレッドをブロックしないように、新しいスレッドを作成します。

それから私は考えました-うーん...100のクライアントが私のサーバープログラムに接続するとしましょう。それぞれに3つのタイマーを作成するので、300のスレッドを実行します。そんなにありませんか?ok300スレッドを実行しているのですか?

次に、AS3ではメインスレッドで実行されると言われました。Timerそして、私は疑問に思いました:どうやって?メインスレッドで実行され、ブロックされないタイマーを実装するにはどうすればよいですか?C ++で可能ですか?

4

7 に答える 7

9

考えられる解決策は、すべてのタイマーに 1 つのスレッドを使用し、タイムアウトによってキューを並べることです。これに関する問題は、タイマーが期限切れになり、コールバック関数を呼び出すと、個別にではなく、グローバル タイマー スレッドのコンテキストで実行されることです。これはもちろん、イベントのためだけに新しいスレッドを生成することで解決できます。次に、直接参加するか、イベントを処理するスレッドプールを使用して参加するため、メインタイマースレッドが「詰まる」ことはありません。

于 2012-09-13T08:35:42.350 に答える
3

単一のタイマー スレッドを作成し、「登録」するクライアントごとにツリーにエントリを作成できます。キーはクライアントのタイムアウトになり、値はクライアントへの参照になります。これにより、タイムアウトによってクライアントが並べ替えられます。

次に、タイマーについては、100ミリ秒ごとなどの周期的なタイマーを設定します(それに応じて調整します)。タイマーが切れたら、タイムアウトした各クライアントを削除してディスパッチするツリーを繰り返します。まだタイムアウトになっていないクライアント タイムアウトに達すると、反復は停止するはずです。

このアプローチのより正確な改善は、タイマーが期限切れになり、クライアントがディスパッチされたときに、次のクライアントのタイムアウトを計算し、それに応じてタイマーを設定することです。それは、ソリューションがどれだけ正確である必要があるかにかかっています。

于 2012-09-13T08:35:31.740 に答える
1

あなたの最初の質問にはすでに十分な答えがあります:タイマーイベントを処理するスレッドプール(たとえば5または10スレッドのセット)はそれを行う通常の方法であり、各イベントの1つのスレッドとすべてのイベントの1つのスレッドの間の適切な妥協点です。

2番目の質問について:通常のプログラミングを使用すると、メインスレッドでタイマーイベントハンドラーを実行できなくなります。可能であれば、メインスレッドを「ブロック」しますが、メインスレッドで実行されているコードからの同意とサポートがなければ、それは不可能です。

メインスレッドは時々停止し、タイマーからのイベントがあるかどうかを確認し、オブジェクトの形式でタイマーからパラメーターを取得してから、イベントを処理する必要があります。この原則を設計する方法はたくさんありますが、それが一般的な方法です。

Unixシステムでは、シグナルの使用も考えられるかもしれませんが、それは良い考えではないと思います。

于 2012-09-13T09:49:51.163 に答える
1

メイン スレッドにタイマーを実装する限り、ユーザー コードから定期的に (イベント ポーリング中などに) 呼び出され、タイマーも処理する何らかのメカニズムが必要です。もちろん、このようなアプローチはおそらく不正確です。メイン スレッドのユーザー コードで許可されている場合にのみタイマーを実行できるからです。

もちろん、コールバック コードの実行中はメイン スレッドもブロックされます。

于 2012-09-13T08:41:14.487 に答える
1

サーバーは、すべてのタイマーに対して 1 つのタイマー スレッドを実行する場合があります。これtimer wheelにより、クライアントのタイマーがサーバーのタイマー ホイールに登録されたときにイベントが作成されます。登録したタイマーがタイムアウトすると、タイマーホイールでイベントが設定されます。クライアントは、タイマーの登録時に作成されたイベントへのハンドルを取得します。クライアントは、登録されたタイマーがタイムアウトになったことを知らせるイベントを待つことができます。このように、スレッドの作成はクライアント次第です。

于 2012-09-13T10:00:24.890 に答える
1

これは設計の問題であるため、誰もが異なる意見を持ち、要件にも依存しますが、IMO、タイマーはスレッドポリシー自体を決定するべきではありません-クライアントが決定する必要があります.

どのような動作が予想されるかはわかりませんが、同じスレッドのタイマーで 300 個のイベントを実行し、1 つのイベント ハンドラーが何らかの理由でブロックされた場合、他のイベント ハンドラーは決してトリガーされません。

1 つの可能性は、スレッドにタイマーを作成することですが、イベント ハンドラーがスレッド プールを介して他のスレッドで実行されるように実装します。もちろん、実行時間の長いハンドラーが多数あると、スレッド プールが使い果たされる可能性があるため、問題が発生する可能性は依然としてあります。

コンテキストの切り替えによりパフォーマンスが低下する可能性があるため、ハンドラーごとに明示的な新しいスレッドを使用しないことを強くお勧めします。スレッド プールは、このバランスを取るのにはるかに適しています。

于 2012-09-13T08:37:10.827 に答える
0

C ++で設計しているので、そのためにBoostASIOタイマーを使用できます。私はそれらに基づいてTimerクラスも設計しましたが、スレッドなしでうまく機能します-OSへの非同期呼び出しを使用するため、基本的には、タイマーの期限が切れたときに呼び出されるコールバックを定義してから、タイマーのasync_waitを呼び出す必要があります非ブロッキングである関数。タイマーオブジェクトを宣言するときは、OSへのASIOインターフェイスであるio_serviceオブジェクトを渡す必要があります。このオブジェクトは非同期リクエストとコールバックの処理を担当するため、ブロッキングメソッドrunを呼び出すことができます。私の場合、メインスレッドをブロックすることができなかったので、この一意の呼び出しがブロックされているスレッドが1つだけありました。

ここでは、BoostASIO非同期タイマーの使用方法の例を見つけることができます。

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();
}
于 2012-11-21T17:30:23.790 に答える