マルチスレッドシミュレーションを可能にするには、クラスをどのように構成すればよいですか?


8

私のゲームでは、建物(家、リソースセンター)のある土地の区画があります。家のような建物には、テナント、部屋、アドオンなどがあり、これらすべての変数に基づいてシミュレーションする必要があるいくつかの値があります。

次に、AndEngineをフロントエンドのものに使用し、シミュレーション計算を実行する別のスレッドを作成します(後でこのスレッドにAIを含めることもできます)。これは、1つのスレッド全体がすべての作業を実行してブロックなどの問題を引き起こすことを防ぐためです。これにより、同時実行性依存性の問題が発生します。

通貨の問題は、私のメインUIスレッドと計算スレッドすべてのシミュレーションオブジェクトにアクセスする場合との両方が必要です。そのため、スレッドセーフにする必要がありますが、それを可能にするためにシミュレーションオブジェクトを格納および構造化する方法がわかりません。

依存関係の問題は、計算値にするために、私の計算は、他のオブジェクトの値に依存していることです。

建物内のテナントオブジェクトを計算にリンクする最良の方法は何ですか?テナントクラスにハードコードしますか?アルゴリズムを簡単に調整できるように「ストア」アルゴリズムを実行するための良い方法は何ですか?

単純な怠惰な方法は、土地の区画(建物などを保持する)など、すべてのオブジェクトを保持するクラスにすべてをまとめることです。このクラスは、ユーザーが利用できるテクノロジーや、スプライトなどのオブジェクトプールなどのゲームの状態も保持します。しかし、これは怠惰で危険な方法ですよね?

編集: 私は依存性注入を見ていたが、それは他のオブジェクトを保持するクラスのようにどれだけうまく対処できますか?つまり、借地人と他の多くの価値を持つ建物のある土地の私の区画。AndEngineの場合も、DIは苦痛のように見えます。


簡単に言うと、アクセスの1つが読み取り専用である場合、データへの同時アクセスについての心配はありません。レンダリングで生データのみを読み取ってレンダリングに使用し、処理中にデータを更新しない限り、問題は発生しません。一方のスレッドはデータを更新し、もう一方のスレッドはデータを読み取ってレンダリングするだけです。
ジェームズ

ユーザーが土地を購入し、その土地に建物を建て、テナントを家に入れることができるため、同時アクセスは依然として問題です。メインスレッドがデータを作成し、データを変更できます。同時アクセスはそれほど問題ではなく、メインスレッドと子スレッドの間の共有のインスタンスに関するものです。
NiffyShibby

依存関係について問題として話します。Googleのような人々は、依存関係を隠すことは賢明ではないと考えています。私のテナントの計算は、建物のプロット、ビルド、画面上でのスプライトの作成に依存します(私は、建物のテナントと他の場所でのテナントのスプライトの作成の間に関係関係がある可能性があります)
NiffyShibby

私の提案は、スレッド化されたものを自己完結型のものにするか、別のスレッドによって管理されているデータへの読み取り専用アクセスを必要とするものにすることとして解釈されるべきだと思います。レンダリングは、スレッド化できるものの例です。データを表示できるように、データへの読み取りアクセスのみが必要です。
James

1
ジェームズ、別のスレッドがそのオブジェクトに変更を加えている最中なら、読み取り専用アクセスでさえ悪い考えであるかもしれません。複雑なデータ構造では、クラッシュが発生する可能性があり、プレーンなデータ型では、一貫性のない読み取りが発生する可能性があります。
カイロタン

回答:


4

問題は本質的にシリアルです-レンダリングする前にシミュレーションの更新を完了する必要があります。シミュレーションを別のスレッドにオフロードするとは、シミュレーションスレッドがティックしている間(つまり、ブロックされていること)にメインUIスレッドが何もないことを意味します。

並行性に関して一般に保持されている「ベストプラクティス」は、提案しているように、レンダリングを1つのスレッドに配置し、シミュレーションを別のスレッドに配置しないことです。実際、私はそのアプローチに反対することを強くお勧めします。2つの操作は自然にシリアルに関連しており、ブルートフォースになる可能性がありますが、最適ではなく、スケーリングもされません

より良いアプローチは、更新またはレンダリングの一部を並行させることですが、更新とレンダリング自体は常にシリアルのままにしておきます。たとえば、シミュレーションに自然な境界がある場合(たとえば、シミュレーションで家が互いに影響し合わない場合)、すべての家をN個の家のバケットに押し込み、それぞれが処理するスレッドの束をスピンアップできます。バケットに入れ、更新ステップが完了する前にそれらのスレッドを結合させます。これは、拡張性が非常に高く、並行設計に適しています。

あなたは問題の残りの部分を考えすぎています:

依存性注入はここでの赤いニシンです。すべての依存性注入は、実際には通常、構築中に、インターフェイスの依存関係をそのインターフェイスのインスタンスに渡す(「注入する」)ことを意味します。

つまり、をモデル化するクラスがありHouse、それが含まれているについて知る必要があるCity場合、Houseコンストラクターは次のようになります。

public House( City containingCity ) {
  m_city = containingCity; // Store in a member variable for later access
  ...
}

特にない。

シングルトンを使用する必要はありません(「エンタープライズ」GUIアプリケーション用に設計された、Caliburnのような非常に複雑で過度に設計された「DIフレームワーク」で行われることがよくあります-これは良いソリューションにはなりません)。実際、シングルトンの導入は、多くの場合、優れた依存関係管理の正反対です。また、通常はロックなしではスレッドセーフにすることができないため、マルチスレッドコードで深刻な問題を引き起こす可能性があります。取得する必要のあるロックが多いほど、問題は並列処理に適していません。


元の投稿ではシングルトンが悪いと言ったことを覚えています...
NiffyShibby

元の投稿ではシングルトンが悪いと言っていたのを覚えていましたが、削除されました。私はあなたが言っていることを理解していると思います。たとえば、私の小さな人が画面上を歩いていると、更新スレッドが呼び出されているため、更新スレッドを更新する必要がありますが、メインスレッドがオブジェクトを使用しているため、他のスレッドがブロックされているため、更新できません。レンダリング間で更新する必要がある場所。
NiffyShibby、2011年

誰かが便利なリンクを送ってくれました。 gamedev.stackexchange.com/questions/95/...
NiffyShibby

5

並行性の問題に対する通常の解決策は、データの分離です。

分離とは、すべてのスレッドが独自のデータを持ち、他のスレッドのデータには触れないことを意味します。この方法では、同時実行性に問題はありませんが、コミュニケーションの問題があります。データを共有しない場合、これらのスレッドはどのように連携できますか?

ここには2つのアプローチがあります。

最初は不変性です。不変の構造/変数は、状態を決して変更しないものです。最初は、これは役に立たないように思えるかもしれません-決して変わらない「変数」をどのように使用できますか?ただし、これらの変数は交換できます。次の例を考えてみますTenant。一貫した状態であることが必要な一連のフィールドを持つクラスがあるとします。TenantスレッドAのオブジェクトを変更し、同時にそれをスレッドBから観察すると、スレッドBは不整合な状態のオブジェクトを見る可能性があります。ただし、Tenantが不変の場合、スレッドAはそれを変更できません。代わりに、新しい Tenant必要に応じてフィールドが設定されたオブジェクトを古いオブジェクトと交換します。スワッピングは1つの参照への変更であり、おそらくアトミックであり、したがって、不整合な状態のオブジェクトを観察する方法はありません。

2番目のアプローチはメッセージングです。その背後にある考え方は、すべてのデータが特定のスレッドによって「所有」されている場合、このスレッドにデータの処理方法を指示できるということです。このアーキテクチャのすべてのスレッドには、メッセージキュー(Messageオブジェクトのリスト、およびメッセージングポンプ)があり、キューからメッセージを削除して解釈し、ハンドラーメソッドを呼び出すメソッドを常に実行しています。たとえば、土地の区画をタップして、土地を購入する必要があることを通知したとします。UIスレッドはPlotオブジェクトを直接変更することはできません。オブジェクトがロジックスレッドに属しているためです(そしておそらく不変です)。そのため、UIスレッドはBuyMessage代わりにオブジェクトを作成し、それをロジックスレッドのキューに追加します。ロジックスレッドは、実行時にキューからメッセージを取得して呼び出しますBuyPlot()、メッセージオブジェクトからパラメータを抽出します。たとえばBuySuccessfulMessage、UIスレッドに「これで土地が増えました」と表示するように指示するメッセージが返される場合があります。画面上のウィンドウ。もちろん、メッセージキューへのアクセスは、ロック、クリティカルセクション、またはAndEngineで呼び出されるものと同期する必要があります。ただし、これはスレッド間の単一の同期点であり、スレッドは非常に短い時間中断されるため、問題ありません。

これら2つのアプローチは、組み合わせて使用​​するのが最適です。スレッドはメッセージと通信し、他のスレッド用に「オープン」な不変データをいくつか持っている必要があります-たとえば、UIがそれらを描画するためのプロットの不変リスト

「読み取り専用」は必ずしも不変を意味するわけではないことにも注意してください!ハッシュテーブルのような複雑なデータ構造は、読み取りアクセス時に内部状態を変更する可能性があるため、まずドキュメントで確認してください。


それはきちんと聞こえます、私はそれを使っていくつかのテストをしなければならないでしょう、それはこのようにかなり高価に聞こえます、私はシングルトンのスコープでDIの線に沿って考え、そして同時アクセスにロックを使用していました。D:しかし、私はこのようにそれをやって、それができた可能性の仕事を考えたことがありません
NiffyShibby

まあ、それは私たちが高度に並行するマルチスレッドサーバーで並行性を行う方法です。おそらく単純なゲームには少々やり過ぎですが、それは私が自分で使うアプローチです。
ネヴァーマインド

4

おそらく、歴史の中で書かれたコンピュータープログラムの99%がたった1つのスレッドを使用し、うまく機能しました。私はAndEngineの経験はありませんが、適切なハードウェアを前提として、スレッド化を必要とするシステムを見つけることは非常にまれです。

従来、1つのスレッドでシミュレーションとGUI /レンダリングを行うには、シミュレーションを少し実行してから、レンダリングを繰り返し、通常は1秒あたり何回か繰り返します。

誰かが複数のプロセスを使用した経験がほとんどない場合、またはスレッドの「安全性」の意味(多くの異なることを意味するあいまいな用語)を十分に理解していない場合、システムに多くのバグを導入するのは簡単です。したがって、個人的には、シングルスレッドアプローチを採用し、シミュレーションとレンダリングをインターリーブし、確実に長い時間がかかり、イベントベースのモデルではなくスレッドが絶対に必要になることがわかっている操作のスレッドを保存することをお勧めします。


Andengineはレンダリングを実行しますが、すべての処理が1つのスレッドで行われる場合、ブロックされないとメインのUIスレッドが遅くなるため、計算は別のスレッドで実行する必要があると感じています。
NiffyShibby、2011年

なぜそう感じますか?通常の3Dゲームよりも高価な計算はありますか?また、ほとんどのAndroidデバイスにはコアが1つしかないため、追加のスレッドから本質的なパフォーマンス上の利点が得られないことを知っていますか?
カイロタン

いいえ、ロジックを分離し、何が行われているのかを明確に定義するのはいいことですが、それを同じスレッドに保持する場合、これが保持されるメインクラスを参照するか、シングルトンスコープでDIを実行する必要があります。これはそれほど問題ではありません。コアに関しては、より多くのデュアルコアandroidデバイスが出てくることがわかります。私のゲームのアイデアは、シングルコアデバイスではまったくうまく機能しない可能性がありますが、デュアルコアではかなりうまく機能する可能性があります。
NiffyShibby、2011年

したがって、1つのスレッド全体ですべてを設計することは、私にとって素晴らしいアイデアのようには思えません。少なくともスレッドを使用すると、ロジックを分離でき、将来的には、最初から設計したようにパフォーマンスを向上させることを心配する必要がありません。
NiffyShibby、2011年

ただし、問題は本質的にシリアルであるため、データを分離していない限り(ロジックスレッドがティックしている間にレンダースレッドに実際に実行させる処理を与えて)、フレームをレンダリングしない限り、両方のスレッドをブロックして、何らかの形でそれらが結合するのを待ちます。シミュレーションの裏側。あなたが説明しているアプローチは、同時実行設計で一般に受け入れられているベストプラクティスではありません。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.