ポーリングが大好きです!私は?はい!私は?はい!私は?はい!私はまだですか?はい!今はどう?はい!
他の人が述べたように、同じ変更されていない状態を何度も戻すためだけにポーリングしている場合、信じられないほど非効率的です。これは、CPUサイクルを燃焼させ、モバイルデバイスのバッテリー寿命を大幅に短縮するためのレシピです。もちろん、必要な速度で新しい意味のある状態を毎回取得するのは無駄ではありません。
しかし、ポーリングが好きな主な理由は、そのシンプルさと予測可能な性質のためです。コードをトレースして、いつ、どこで、どのスレッドで発生するかを簡単に確認できます。理論的には、世論調査がごくわずかな無駄である世界に住んでいたなら(現実はそこから遠く離れているとはいえ)、コードを維持することは大したことではないでしょう。そして、この場合はすべきではないが、パフォーマンスを無視できるかどうかを確認するように、ポーリングとプルの利点があります。
DOS時代にプログラミングを始めたとき、私の小さなゲームはポーリングを中心に展開していました。キーボード割り込みに関連してほとんど理解できなかった本からいくつかのアセンブリコードをコピーし、キーボード状態のバッファを保存するようにしました。この時点で、メインループは常にポーリングされていました。上キーが押されていませんか?いや。上キーが押されていませんか?いや。今はどう?いや。今?はい。さて、プレーヤーを動かします。
信じられないほど無駄ですが、最近のマルチタスクやイベント駆動型プログラミングに比べて、非常に簡単に推論できることがわかりました。いつ、どこで問題が発生するかを正確に知っていたので、フレームレートを安定して予測可能に保つことが、しゃっくりなしに簡単になりました。
それ以来、条件変数を使用してスレッドに通知して新しい状態を取得できるポイントを通知するなど、CPUサイクルを実際に燃やすことなく、その利点と予測可能性を得る方法を常に探しています彼らのことをして、再び通知されるのを待って眠りに戻ります。
そして、どういうわけか、イベントキューは少なくともオブザーバーパターンよりも作業しやすいと思いますが、それでもあなたがどこに行くのか、何が起こるのかを予測するのはそれほど簡単ではありません。少なくともイベント処理制御フローをシステム内のいくつかの重要な領域に集中させ、1つの関数から中央のイベント処理スレッドの外部で突然完全にリモートで予期しない場所にバウンスするのではなく、常に同じスレッドでそれらのイベントを処理します。したがって、二分法は常にオブザーバーとポーリングの間にある必要はありません。イベントキューは一種の妥協点です。
しかし、ええ、どういうわけか、私は何年も前にポーリングしていたときに使用していた予測可能な制御フローに類推的に近いことを行うシステムについて推論するのがはるかに簡単になりますが、状態の変更が発生していない時間。したがって、条件変数のようにCPUサイクルを不必要に燃やさない方法でそれを行うことができれば、その利点があります。
同種ループ
Josh Caswell
わかりました、私の答えにいくつかの愚かさを指摘したことから素晴らしいコメントを得ました:
「スレッドに通知するために条件変数を使用するような」ポーリングではなく、イベントベース/オブザーバーの配置のように聞こえる
技術的には、条件変数自体がオブザーバーパターンを適用してスレッドをウェイクアップ/通知するため、「ポーリング」と呼ぶのはおそらく誤解を招く可能性があります。しかし、DOSの日からのポーリングと同様の利点が得られます(制御フローと予測可能性の点で)。もっと説明しようと思います。
当時魅力的だったのは、コードの一部を見たり、トレースしたりして、「さて、このセクション全体がキーボードイベントの処理専用です。このコードのセクションでは他に何も起こらない」ということです。そして、私は前に何が起こるかを正確に知っており、その後に何が起こるかを正確に知っています(物理学やレンダリングなど)。」キーボード状態のポーリングにより、この外部イベントへの応答で何をすべきかを処理する限り、制御フローのそのような集中化が実現しました。この外部イベントにはすぐに応答しませんでした。私たちは都合の良いときにそれに応じました。
オブザーバーパターンに基づいたプッシュベースのシステムを使用すると、多くの場合それらの利点が失われます。コントロールのサイズが変更されると、サイズ変更イベントがトリガーされます。それをトレースすると、より多くのイベントをトリガーするサイズ変更で多くのカスタム処理を行うエキゾチックなコントロールの中にいることがわかります。私たちは、システム内のどこに行き着くのかについて、これらすべてのカスケードイベントをトレースすることに完全に驚きました。さらに、スレッドAはここでコントロールのサイズを変更し、スレッドBは後でコントロールのサイズを変更する可能性があるため、これらのすべてが特定のスレッドで一貫して発生しないこともあります。だから、すべてがどこで起こるのか、何が起こるのかを予測するのがどれほど難しいかを考えると、私はいつもこれを推論するのが非常に難しいと感じました。
イベントキューは、少なくともこれらのすべてのことがスレッドレベルで発生する場所を単純化するため、推論するのが少し簡単です。しかし、多くの異なることが起こっている可能性があります。イベントキューには、処理するイベントの折mixture的な混合物を含めることができますが、各イベントは、発生したイベントのカスケード、処理された順序、およびコードベース内のあらゆる場所でバウンスする方法について、まだ驚かされる可能性があります。
ポーリングに「最も近い」と考えているものは、イベントキューを使用しませんが、非常に均質なタイプの処理を延期します。Aは、PaintSystem
それが適切なzオーダーでその内部の細胞や再描画のすべてを通じて、単純なシーケンシャルループを実行し、その時点でウィンドウの特定のグリッドセルを、再描画するためにやるべき仕事が絵だと条件変数を介して警告を受ける可能性があります。再描画が必要なセルに存在する各ウィジェットでペイントイベントをトリガーするために、ここで間接呼び出し/動的ディスパッチの1つのレベルがあるかもしれませんが、それだけです-間接呼び出しの1つのレイヤーだけです。条件変数は警告するオブザーバーパターン使用していますPaintSystem
、それはやるべき仕事を持っていることを、それはより多くのそれよりも何も指定せず、PaintSystem
その時点で、1つの均一で非常に均質なタスクに専念しています。PaintSystem's
コードをデバッグおよびトレースしているとき、ペイント以外は何も起こらないことがわかっています。
そのため、イベントキュー処理で得られるような多数の責任を実行する異種のデータ上の非均質ループではなく、非常に特異な責任を適用するデータに対して均質ループを実行するこれらのものがある場所までシステムをダウンさせることです。
私たちはこのタイプのことを目指しています:
when there's work to do:
for each thing:
apply a very specific and uniform operation to the thing
とは対照的に:
when one specific event happens:
do something with relevant thing
in relevant thing's event:
do some more things
in thing1's triggered by thing's event:
do some more things
in thing2's event triggerd by thing's event:
do some more things:
in thing3's event triggered by thing2's event:
do some more things
in thing4's event triggered by thing1's event:
cause a side effect which shouldn't be happening
in this order or from this thread.
などなど。また、タスクごとに1つのスレッドである必要はありません。1つのスレッドは、GUIコントロールのレイアウト(サイズ変更/再配置)ロジックを適用して再描画できますが、キーボードやマウスのクリックを処理できない場合があります。したがって、これはイベントキューの同質性を改善するだけと見なすことができます。ただし、イベントキューを使用して、サイズ変更および描画機能をインターリーブする必要もありません。私たちは次のことができます:
in thread dedicated to layout and painting:
when there's work to do:
for each widget that needs resizing/reposition:
resize/reposition thing to target size/position
mark appropriate grid cells as needing repainting
for each grid cell that needs repainting:
repaint cell
go back to sleep
したがって、上記のアプローチは、実行する作業があるときにスレッドに通知するために条件変数を使用するだけですが、異なるタイプのイベントをインターリーブしません(1つのループでサイズ変更、別のループでペイント、両方の混合ではありません)実行する必要のある作業が正確に何であるかをわざわざ伝える必要があります(スレッドは、システム全体のECSの状態を調べることで目覚めると「検出」します)。実行される各ループは本質的に非常に均質であり、すべてが発生する順序について簡単に推論できます。
このタイプのアプローチを何と呼ぶべきかわかりません。他のGUIエンジンがこれを行うのを見たことがなく、それは私の独自のエキゾチックなアプローチのようなものです。しかし、オブザーバーまたはイベントキューを使用してマルチスレッドGUIフレームワークを実装しようとする前に、それをデバッグするのは非常に困難でした。ソリューションについて(一部の人はこれを行うことができますが、私は十分にスマートではありません)。私の最初の反復設計では、信号を介してスロットを直接呼び出しましたが、一部のスロットは非同期作業を行うために他のスレッドを生成しました。これは最も推論が難しく、競合状態とデッドロックにつまずいていました。2回目のイテレーションではイベントキューを使用しましたが、これは少し簡単に推論できますが、しかし、あいまいなデッドロックや競合状態に陥ることなく、脳がそれを行うのは簡単ではありません。3回目と最後の反復では、上記のアプローチを使用し、最終的には、私のような愚かな単純なものでも正しく実装できるマルチスレッドGUIフレームワークを作成できました。
次に、このタイプの最終的なマルチスレッドGUI設計により、私が推論するのが非常に簡単で、私が犯しがちだったタイプの間違いを避けることができる他の何かを考え出すことができました。少なくとも、これらの同種のループと、DOS時代にポーリングしていたときと似た制御フローに似ているためです(実際にはポーリングではなく、作業があるときにのみ作業を実行します)。アイデアは、イベント処理モデルから可能な限り遠くに移動することでした。これは、非均一ループ、非均一副作用、非均一制御フローを意味し、均一データで均一に動作し、分離する均一ループにますます取り組むことでした。副作用を「何」に集中しやすくする方法で統一します