3

私は、Bjarne Stroustrup による The C++ Programming Language のいくつかの演習に取り組んでいます。第 12 章の最後の問題 11 で混乱しています。

(*5) イベント駆動型シミュレーションを記述するためのライブラリを設計および実装します。ヒント: <task.h>。... クラス task のオブジェクトは、その状態を保存し、その状態を復元して、コルーチンとして動作できるようにする必要があります。特定のタスクは、タスクから派生したクラスのオブジェクトとして定義できます。タスクによって実行されるプログラムは、仮想関数として定義される場合があります。... 仮想時間の概念を実装するスケジューラが必要です。... タスクは通信する必要があります。そのためのクラス キューを設計します。...

これが何を求めているのか正確にはわかりません。タスクは別のスレッドですか? (私が知る限り、システム コールなしで新しいスレッドを作成することはできません。また、これは C++ に関する本なので、それが意図されているとは思いません。) 割り込みがなければ、実行中のスレッドを開始および停止するにはどうすればよいですか?関数?これには、ビジー待機 (つまり、継続的にループして条件をチェックする) が含まれると思いますが、しばらくの間終了しない可能性がある関数にそれを適用する方法はわかりません (たとえば、無限ループが含まれている場合)。 .

編集:詳細については、以下の私の投稿を参照してください。

4

8 に答える 8

5

「イベント駆動型シミュレーション」についての私の理解は次のとおりです。

  • コントローラはイベント キューを処理し、イベントが特定の時間に発生するようにスケジュールしてから、キューの一番上のイベントを実行します。
  • イベントは、スケジュールされた時刻に即座に発生します。たとえば、「移動」イベントは、現在のシミュレーション時間で状態ベクトルが有効になるように、シミュレーション内のエンティティの位置と状態を更新します。「センス」イベントは、すべてのエンティティの状態が現時点であることを確認し、現在のエンティティが他のエンティティをどれだけうまく感知できるかを評価するために、いくつかの数学的モデルを使用する必要があります。(ロボットがボード上を動き回ると考えてください。)
  • このように、時間はイベントからイベントへとジャンプしながら不連続に進みます。これを時間駆動型シミュレーションと比較してください。時間が離散的なステップで移動し、すべてのエンティティの状態が時間ステップごとに更新されます (ほとんどの Simulink モデル)。
  • その後、イベントは自然な速度で発生します。通常、シミュレーションですべてのデータを最高速度で再計算することは意味がありません。

ほとんどのプロダクション イベント ドリブン シミュレーションは、シングル スレッドで実行されます。それらはその性質上複雑になる可能性があるため、マルチスレッド シミュレーションを同期しようとすると、指数関数的な複雑さのレイヤーが追加される傾向があります。そうは言っても、事前定義された TCP メッセージを使用してプロセス間でデータを送信する、Distributive Interactive Simulation (DIS)と呼ばれるマルチプロセスの軍事シミュレーションの標準があります。

編集: モデリングとシミュレーションの違いを定義することが重要です。モデルは、システムまたはプロセスの数学的表現です。シミュレーションは、一定期間実行される 1 つ以上のモデルから構築されます。ここでも、イベント ドリブン シミュレーションはイベントからイベントへとホップしますが、時間ドリブン シミュレーションは一定のタイム ステップで進行します。

于 2008-12-16T01:20:37.030 に答える
3

ヒント: <task.h>。

は、 CFront の初期バージョンに同梱されていた古い共同マルチタスク ライブラリへの参照です (そのページからダウンロードすることもできます)。

「 A Set of C++ Classes for Co-routine Style Programming 」という論文を読むと、物事がより理にかなっているでしょう。


少し追加:

私は、タスク ライブラリを使用するのに十分な年齢のプログラマではありません。しかし、C++ は、Stroustrup が Simula でタスク ライブラリと同じプロパティの多くを備えたシミュレーションを作成した後に設計されたことを知っているので、常に興味を持っていました。

本の演習を実装する場合、おそらく次のようにします (このコードをテストしたり、コンパイルしようとしたりしていないことに注意してください)。

class Scheduler {
    std::list<*ITask> tasks;
  public:
    void run()
    {
        while (1) // or at least until some message is sent to stop running
            for (std::list<*ITask>::iterator itor = tasks.begin()
                      , std::list<*ITask>::iterator end = tasks.end()
                    ; itor != end
                    ; ++itor)
                (*itor)->run(); // yes, two dereferences
    }

    void add_task(ITask* task)
    {
        tasks.push_back(task);
    }
};

struct ITask {
    virtual ~ITask() { }
    virtual void run() = 0;
};

私は、人々が私の選択のいくつかに同意しないことを知っています. たとえば、インターフェイスに構造体を使用します。しかし、構造体には、それらからの継承がデフォルトでパブリックであるという動作があり (クラスからの継承はデフォルトでプライベートです)、インターフェイスからプライベートに継承することに価値がないと思うので、パブリック継承をデフォルトにしないのはなぜですか?

ITask::run() への呼び出しは、タスクが中断できるポイントに到達するまでスケジューラをブロックし、その時点でタスクが run メソッドから戻り、スケジューラが run を再度呼び出すまで待機するという考え方です。継続する。「協調的マルチタスク」の「協調的」とは、「タスクがいつ中断できるかを示す」という意味です (「コルーチン」は通常、「協調的マルチタスク」を意味します)。単純なタスクはその run() メソッドで 1 つのことしか実行できず、より複雑なタスクはステート マシンを実装し、その run() メソッドを使用してオブジェクトの現在の状態を把握し、それに基づいて他のメソッドを呼び出すことができます。その状態で。タスクこれが機能するためには、ときどきコントロールを手放してください。それが「協調的マルチタスク」の定義だからです。これは、最新のすべてのオペレーティング システムが協調マルチタスクを使用しない理由でもあります。

この実装は、(1) 公平なスケジューリングに従いません (おそらく、タスクの run() メソッドで費やされたクロック ティックの現在の合計を保持し、他のタスクが「追いつく」まで、他のタスクに比べてあまりにも多くの時間を使用したタスクをスキップします)。 、(2)タスクを削除できるようにするか、(3)スケジューラを停止できるようにします。

タスク間の通信に関しては、Plan 9 の libtaskまたはRob Pike の newsqueakを参照してインスピレーションを得ることができます (「Newsqueak の UNIX 実装」のダウンロードには、興味深い仮想マシンでのメッセージ パッシングについて説明している論文「Newsqueak の実装」が含まれています)。

しかし、これはストラウストラップが念頭に置いていた基本的な骨格だと思います。

于 2008-12-16T00:25:11.743 に答える
2

離散イベント シミュレーションの一般化された構造は、時間値をキーとする優先キューに基づいています。大まかに言えば、次のようになります。

    While (終了条件ではない):
        プライオリティ キューから次のイベント (時間が最も短いもの) をポップします。
        そのイベントを処理します。これにより、より多くのイベントが生成される可能性があります
        新しいイベントが生成された場合:
            これを、生成された時間にキー付けされた優先キューに配置します

コルーチンは、モデルのビューをイベント中心からエンティティ中心に変更します。エンティティは、いくつかのライフサイクルを経ることができます (たとえば、ジョブを受け入れ、リソース X を取得し、ジョブを処理し、リソース X を解放し、次のステップのためにジョブをキューに入れます)。グラブ リソースはセマフォのような同期プリミティブで処理されるため、これはプログラミングがやや簡単です。ジョブと同期プリミティブはイベントを生成し、バックグラウンドでキューに入れます。

これにより、オペレーティング システムのプロセスと概念的に類似したモデルが得られ、スケジューラはプロセスの入力または要求された共有リソースが利用可能になったときにプロセスをウェイクアップします。コルーチン モデルにより、シミュレーションが非常に理解しやすくなり、複雑なシステムのシミュレーションに役立ちます。

于 2008-12-17T14:15:52.847 に答える
1

コルーチンベースのフレームワーク(同名のDEMOS)を説明するDEMOS (Simulaの離散イベントモデリング)と呼ばれる本とフレームワークがあります。30年ほど前のDEMOSは、実際には非常に優れたシステムであり、GrahamBirtwistleは非常に優れた人物です。

C ++でコルーチンを実装する場合(setjump / longjumpを考えてください)、この本を見て、本当に本当にエレガントな離散イベントモデリングフレームワークの説明を確認する必要があります。30年前ですが、時代を超越したクラシックであり、ファン層はまだ残っています。

于 2008-12-17T13:37:23.127 に答える
1

これは、SottieT812の回答に対するtitaniumdecoyのコメントへの応答です。コメントするには大きすぎるので、別の答えにすることにしました。

シミュレーション状態はイベントに応答してのみ変化するという意味で、イベント駆動型です。たとえば、ミサイル発射ミサイル衝撃の2つのイベントがあるとします。起動イベントが実行されると、いつどこで影響が発生するかがわかり、適切な時間に影響イベントがスケジュールされます。ミサイルの位置は、発射と衝撃の間で計算されませんが、特定の時間に位置を取得するために他のオブジェクトから呼び出すことができるメソッドがある可能性があります。

これは、ミサイル(およびシミュレーション内の他のすべてのオブジェクト)の正確な位置がすべてのタイムステップ、たとえば1秒後に計算される時間駆動シミュレーションとは対照的です。

モデルの特性、必要な回答の忠実度、およびその他の多くの要因に応じて、イベント駆動型または時間駆動型のシミュレーションの方がパフォーマンスが向上する場合があります。

編集:誰かがもっと学ぶことに興味があるなら、冬のシミュレーション会議からの論文をチェックしてください

于 2008-12-16T13:14:29.617 に答える
1

(私は C++ 開発者ではありません)

おそらくそれが意味するのは、ほとんどがコールバック関数ポインターとスケジュールされた時間で構成され、Scheduler クラスのリストに格納できるクラス Task (Event のように) を作成する必要があることです。これは基本的に保持する必要があります。タイム カウンターを追跡し、時間になると各タスクの関数を呼び出します。これらのタスクは、シミュレーションのオブジェクトによって作成される必要があります。

離散シミュレーション側でヘルプが必要な場合は、先に進んで質問を編集してください。

于 2008-12-15T22:47:31.580 に答える
0

task.h クラスについて説明している「me.yahoo.com/...」でリンクされている論文:

  1. タスクは並行して実行されます
  2. タスクは一時停止され、後で再開される場合があります

ライブラリは、マルチプログラミングの方法として記述されています。

スレッドや別のプロセスを使用せずにこれを行うことは可能ですか?

于 2008-12-16T03:28:41.653 に答える