0

現在、入力データからモデルを構築し、そのデータをユーザーに表示するアプリケーションの再設計に取り組んでいます。現在のシステムには、モデルを構築するためのスレッド、モデルの視覚化を構築するためのスレッド、および視覚化を表示するスレッドがあります。私が抱えている問題は、モデリングと視覚化のスレッド間でポインターが渡されることです。スレッドを安全にするために、モデル内のすべてのオブジェクトにミューテックスが必要です。これは、システム内でアクティブなミューテックスが数千あり、両方のスレッドがリソースを競合するため、多くのストールが発生していることを意味します。

これら 3 つのスレッドが存在することを考えると、効率的かつスレッド セーフな方法でモデリング スレッドとビジュアライゼーション スレッド間でデータを共有する最善の方法は何でしょうか? 一部のデータ構造は大きく、モデリング スレッドのサイクルごとに変更されるため、パスごとにデータのコピーを作成するのは少し気が進まない。

編集:

私たちが望んでいるシステム全体の合計遅延は、ディスプレイの変化を示すディスプレイへのメッセージを受信して​​から最大 100 ミリ秒になることです。可能であれば高速化を望んでいますが、より重要なことは、一貫性を保つ必要があることです。現在、ミューテックスの競合により、サイクル タイムに大きなばらつきが見られます。モデリングからビジュアライゼーションに移行するデータは、2D 高さマップ (最大 18000 セル相当のデータ) によって支配されています。ただし、モデルの更新で更新されるセルの実際の数は大幅に少なく、おそらくわずか数百です。

4

2 に答える 2

2

私は、メッセージ投稿/メッセージ ポンプ アーキテクチャの大ファンです。これは、スレッド間でデータを通信するために MFC/Win32 が提供する主な方法です。このアーキテクチャはスレッド メッセージからイベント駆動されるため、受信スレッドがスレッド メッセージを処理している場合、スレッド間の通信用に明示的に新しく作成されたデータを処理しています (以下の例を参照)。

これを自分で実装し、ロックを各スレッドのスレッド メッセージの個別のリストにローカライズできます。したがって、あるスレッドが別のスレッドにメッセージを送信したい場合は、おおよそ次のようにします。

PostThreadMessage(msg, void* param, int paramSize)
{
    lock(msgQueueLock);

    // may wish to copy param
    char paramCpy = malloc
    msgQueue.Queue(msg, pparam, paramSize); 

    unlock(msgQueueLock);
}

スレッドのメインループはただ

// thread's msg pump
while (1)
{
    // can also use condition var to wait for queue to change...
    lock(msgQueueLock);
    HandleMsgLocally(msgQueue.Deque())
    unlock(msgQueueLock);
}

とにかく、MVC に戻ると、モデルで何かが変更された場合、ビューに投稿して特定のフィールドを更新することができます。

// Well known msg name
int msgName = MODEL_FIELD_A_UPDATED

...

void Model::UpdateFieldA(int newVal)
{
    int* valToCommunicate = new int(newVal)
    PostThreadMessage(MODEL_FIELD_A_UPDATED, valToCommunicate, sizeof(int))
}


...
void HandleMsgLocally(...void * param,)
{
    if (msg == MODEL_FIELD_A_UPDATED)
    {
       int* val = reinterpret_cast<int*>(param);
       //... process param
       delete val;
    }
}

利点は、ロックをローカライズできることです。これは巨大です。また、パラメーターが送信者によって明示的に新規作成され、受信者によって削除されることが理解されている限り、共有メモリへのアクセスについて心配する必要はありません。

これには多くの欠点がありますが、レイテンシーはその 1 つです。何かが変わったことをすぐに知る必要がある場合は、実際にその共有データを作成し、最適なロック スキームを考える必要があります。複数の方向からいつでも更新できるグローバルな状態が本当に必要な場合は、この場合、私の本ではシングルトンで問題ありません。この設計は、一方向のデータに最も適しています。競合状態になる可能性があります。ただし、あるスレッドから別のスレッドへのインスペクション用にロッキング ゲッターを実装することもできますが、投稿する値を設定する必要があります。考えるべき変数はたくさんありますが、うまくいけば、これがあなたの助けになるでしょう。また、メッセージ内のパラメーターを削除するのを忘れることがあります。

編集に基づいて更新 あなたの情報に基づいて、データの量に応じて、投稿は非常にうまくいく可能性があります。コードのプロファイリングは重要です。私があなただったら、いくつかの概念実証を試して、条件変数を使用して同期性を管理することに切り替えます。Windows プラットフォームを使用している場合は、必ずメッセージ ポンプを使用してください。

于 2009-02-17T16:57:36.733 に答える
2

あなたからのより詳細なデータがなければ意味のある方法でこれに答えるのが難しいことについてはすでにコメントしましたが、とにかくここに2つのヒントがあります:

  • 視覚化を必要以上に頻繁に更新しないでください。ビジュアライゼーションの複雑さに応じて、表示を 1 秒あたり 3 ~ 5 回の更新に制限する必要があります。そのため、モデリング スレッドがより速く進行する場合は、すべての反復を表示しないでください。視覚化スレッドが x ミリ秒ごとに新しいデータを要求するようにするか、モデリング スレッドが最後の視覚化から十分な時間が経過してから新しい視覚化を要求するようにすることができます。

  • 回避できる場合は、ロックを使用しないでください。不変データへの共有ポインターを使用すると、オブジェクトをロックする必要がないため (すべてのアクセスは読み取り専用になります)、多くのスレッド競合が解消されます。きめの細かいクラス設計により、モデリング ループごとに実際に変化する部分にデータをコピーする必要性も制限されます。ただし、これには現在の設計に多くの変更が必要になる場合があります。しかし、これは本当に価値があることがわかりました。

編集:編集後、できるだけ多くのデータをできるだけ早く表示したいが、変更するのは全体のデータの 5% だけであることを考えると、ロックを可能な限り排除することをお勧めします。

セルの変更から変更されたデータに基づく新しいセルの作成にアルゴリズムを変更し、モデルをまったく変更せずに、変更されていないセルにスマート ポインターをコピーし、新しいセル オブジェクトを作成するだけで新しいモデルを作成する場合残りの数千のオブジェクトのいずれにもロックは必要ありません。完成したモデルは視覚化スレッドに渡すことができ、新しいモデルをすぐに作成できます。同様に、視覚化スレッドとそれがモデルから作成するオブジェクトについても、GUI スレッドに渡すことができ、その時点で現在のモデルから新しいオブジェクトを作成できます。一部のセルは複数のモデルの一部になり、一部のセルは 1 つのモデルのみの一部になります。ビジュアライゼーションを作成し、出力ディスプレイにレンダリングするためのオブジェクトも、セルを共有できます。

プログラムに残る唯一のロックは、現在のモデル (または他の同様のトップレベル オブジェクト) へのアクセスを同期するための、スレッドごとに 1 つのトップレベル ロックです。実行される操作は非常に短いため、レイテンシは問題になりません。それに加えて、この設計では複数のプロセッサまたはコアを最大限に活用できますが、メモリ消費量と CPU サイクルがわずかに増加します。ただし、これは、ますます並列化が進む現在および将来のハードウェアでソフトウェアのパフォーマンスを向上させるための最良の方法です。

于 2009-02-17T17:07:45.383 に答える