Unityコルーチンとスレッド


13

コルーチンとスレッドの違いは何ですか?一方を他方より使用する利点はありますか?


3
メインスレッドで動作するコルーチン。
ウォルタス


2
スレッドでUnity関数を使用することはできません。コルーチンはできます。
ヘリウム


1
@Hellium修正、別のスレッドでUnity関数を使用することはできません。つまり、Unity関数はメインスレッドで使用できます
ファラプ

回答:


24

コルーチンは一見スレッドのように機能するように見えますが、実際にはマルチスレッドを使用していません。それらは、それらが実行されるまで順次実行されyieldます。エンジンは、独自のメインループの一部として、(ポイントが正確の種類に依存するもので、すべてが得られたコルーチンチェックしますyield詳細については、この図をチェックし、)彼らは彼らの次なるまで次々と継続yieldした後、メインループを進めます。

この手法には、実際のマルチスレッドの問題に起因する頭痛のないコルーチンを使用できるという利点があります。コンテキストスイッチによって引き起こされるデッドロック、競合状態、パフォーマンスの問題は発生しません。適切にデバッグでき、スレッドセーフなデータコンテナを使用する必要はありません。これは、コルーチンが実行されているとき、Unityエンジンが制御された状態にあるためです。Unityのほとんどの機能を使用しても安全です。

一方、スレッドを使用すると、現時点でUnityメインループがどの状態にあるかをまったく知ることができません(実際にはまったく動作していない可能性があります)。そのため、スレッドは、そのことを行うべきではないときに一度に何かを行うことで、かなりの大混乱を引き起こす可能性があります。サブスレッドからネイティブのUnity機能に触れないでください。サブスレッドとメインスレッドの間で通信する必要がある場合は、スレッドにスレッドセーフ(!)コンテナーオブジェクトに書き込み、MonoBehaviourに通常のUnityイベント機能中にその情報を読み取らせます。

「実際の」マルチスレッドを行わないことの欠点は、コルーチンを使用して、複数のCPUコアでCPU集中計算を並列化できないことです。ただし、これらを使用して、計算を複数の更新に分割できます。したがって、ゲームを1秒間フリーズする代わりに、数秒間で平均フレームレートを下げるだけです。しかし、その場合yield、Unityに更新を実行させたいときはいつでもコルーチンに責任があります。

結論:

  • 非同期実行を使用してゲームロジックを表現する場合は、コルーチンを使用します。
  • 非同期実行を使用して複数のCPUコアを使用する場合は、スレッドを使用します。

コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
マイケルハウス

10

コルーチンは、コンピューターサイエンスで私たちが「協調的マルチタスク」と呼ぶものです。これらは、複数の異なる実行ストリームが互いに協調してインターリーブする方法です。協調型マルチタスクでは、実行の1つのストリームは、CPUの所有権が議論の余地のないものになるまで続きyieldます。その時点で、Unity(または使用しているフレームワーク)には、実行の別のストリームに切り替えるオプションがあります。また、それは、それが実行されるまで、CPUの唯一の議論の余地のない所有権を取得しyieldます。

スレッドは、「プリエンプティブマルチタスク」と呼ばれるものです。スレッドを使用している場合、フレームワークはいつでもスレッドを途中で停止して別のスレッドに切り替える権利を留保します。どこにいても問題ありません。場合によっては、変数をメモリに書き込む途中で停止することさえあります!

それぞれに長所と短所があります。コルーチンの短所は、おそらく最も理解しやすいでしょう。まず、コルーチンはすべて単一のコアで実行されます。クアッドコアCPUを使用している場合、コルーチンは4つのコアのうち1つのみを使用します。これは物事を単純化しますが、場合によってはパフォーマンスの問題になる可能性があります。2つ目の欠点は、コルーチンがを拒否するだけでプログラム全体を停止できることに注意する必要があることですyield。これは、何年も前のMac OS9の問題でした。OS9は、コンピューター全体で協調的なマルチタスクのみをサポートしていました。プログラムの1つがハングすると、コンピューターが非常に激しく停止し、OSがエラーメッセージのテキストをレンダリングすることさえできず、何が起こったのかを知ることができません。

コルーチンの長所は、コルーチンが比較的理解しやすいことです。発生するエラーははるかに予測可能です。また、通常、必要なリソースが少なくなります。これは、何万ものコルーチンまたはスレッドに到達するのに役立ちます。Candid Moonはコメントで、もしあなたがスレッドを適切に研究していなければ、コルーチンに固執するだけで、それは正しいと述べた。コルーチンの操作ははるかに簡単です。

スレッドはまったく別物です。別のスレッドがいつでもあなた割り込む可能性に対して常に警戒する必要がありますそしてあなたのデータを混乱させます。スレッディングライブラリは、これを支援する強力なツールスイート全体を提供します。たとえば、ミューテックスや条件変数は、OSが他のスレッドの1つを実行しても安全かどうかをOSに伝えるのに役立ちます。これらのツールを適切に使用する方法に特化したコース全体があります。発生する有名な問題の1つは「デッドロック」です。これは、2つのスレッドが両方とも「スタック」して、他方がいくつかのリソースを解放するのを待機する場合です。Unityにとって非常に重要な別の問題は、多くのライブラリ(Unityなど)が複数のスレッドからの呼び出しをサポートするように設計されていないことです。どの呼び出しが許可され、どの呼び出しが禁止されているかに注意を払わないと、フレームワークを簡単に破ることができます。

この余分な複雑さの理由は、実際には非常に単純です。プリエンプティブマルチタスクモデルは、他のスレッドに割り込みをかけるだけでなく、異なるコアでスレッドを実際に並行して実行できるマルチスレッドモデルに本当に似ています。これは信じられないほど強力で、出てくるこれらの新しいクアッドコアと16進コードCPUを実際に活用する唯一の方法ですが、pandorasボックスを開きます。マルチスレッドの世界でこのデータを管理する方法の同期ルールは非常に残酷です。C ++の世界MEMORY_ORDER_CONSUMEでは、マルチスレッド同期の1つ、1つ、1つ、1つ、1つ、2つのコーナーに関する記事全体があります。

それでは、スレッド化の短所は?シンプル:彼らは難しいです。これまでに見たことがないバグのクラス全体に出くわすことができます。多くは、いわゆる「ヘイゼンバグ」であり、時々表示され、デバッグすると消えます。これらに対処するために提供されるツールは非常に強力ですが、低レベルでもあります。使いやすいように設計されているのではなく、最新のチップのアーキテクチャ上で効率的になるように設計されています。

ただし、すべてのCPUパワーを使用する場合は、必要なツールです。また、割り込みが発生する可能性のあるすべての質問をOSに処理させるため、マルチスレッドではコルーチンよりも理解しやすいアルゴリズム実際にあります。

コルーチンにこだわる率直な月のコメントも、私の推薦です。スレッドのパワーが必要な場合は、コミットしてください。出て行くと、本当に正式に、スレッドを学びます。スレッドについて考える最良の方法を整理し、安全で信頼性の高い再現可能な結果を​​早期に取得し、パフォーマンスを向上させる方法を見つけ出すために、数十年来ました。たとえば、すべての健全なコースは、条件変数を教える前にミューテックスを教えます。アトミックをカバーするすべての健全なコースは、アトミックが存在することに言及する前に、ミューテックスと条件変数を完全に教えます。(注:アトミックに関する健全なチュートリアルなどはありません。)スレッド化の断片を学びましょう。片頭痛を懇願しています。


スレッドは、主に操作と依存関係がインターリーブされている場合に複雑さを追加します。メインゲームループにx()、y()、およびz()の3つの関数が含まれ、それらのどれもが他の人が必要とするものに影響しない場合、3つすべてを同時に開始しますが、次に進む前にすべてが完了するのを待機しますあまり複雑にせずに、マルチコアマシンのメリットを享受できます。条件変数は一般にミューテックスと組み合わせて使用​​されますが、独立並列ステップパラダイムでは、ミューテックス
自体

...ただし、y()およびz()を処理するスレッドがトリガーされるまで待機する方法、およびx()を実行した後、y()およびzまでメインスレッドが待機する方法()完了しました。
-supercat

@supercat 独立したパラレルフェーズとシーケンシャルフェーズとの間を移行するには常に何らかの同期が必要です。時々それはa以外の何物でもありjoin()ませんが、何かが必要です。同期を行うシステムを設計したアーキテクトがいない場合は、自分で作成する必要があります。マルチスレッドを行う人として、コンピューターの動作方法に関する人々のメンタルモデルは、同期を安全に行う前に調整する必要があります(これが良いコースです)
Cort Ammon-Reinstate Monica

もちろん、何らかの同期が必要であり、最高の効率を達成するには一般にを超えるものが必要になりjoinます。私のポイントは、スレッド化のパフォーマンスの利点の適度な割合を追求することは、より複雑なスレッド化アプローチを使用してより大きな割合を獲得するよりも簡単な場合があることです。
supercat

新しいタイプのバグについて:コードが安全であることを論理的に証明できない場合、それがどの程度バグがあるかを知ることは不可能です。実行順序は未定義でもかまいませんが、1台のマシンで実行するたびに同様のパスを取ることがよくあります。多くの場合、一部のマシンでハード障害が発生することが多く、可能性のあるすべてのマシンでテストすることはできません。職場では、ログがオフになっている場合にのみ、1台のマシンでしか現れないバグがありました。複数の人がそれを追跡するのに長い時間がかかりました。野生でそれを望まないでください。同期をテストするだけでなく、論理的に証明してください。
アーロン

2

可能な限り単純な用語で...


スレッド

スレッドはいつ生成されるかを決定せず、オペレーティングシステム(「OS」、Windowsなど)がスレッドを生成するタイミングを決定します。オペレーティングシステムは、スレッドのスケジューリングをほぼ完全に担当し、実行するスレッド、スレッドを実行するタイミング、および期間を決定します。

さらに、スレッドは同期的に(スレッドごとに)または非同期的に(異なるCPUコアで異なるスレッドが実行される)実行される場合があります。非同期で実行できるということは、スレッドが同じ時間でより多くの作業を実行できることを意味します(スレッドは文字通り同時に2つのことを実行しているため)。OSがそれらをスケジューリングするのが得意であれば、同期スレッドでさえ多くの作業を行います。

ただし、この追加の処理能力には副作用が伴います。たとえば、2つのスレッドが同じリソース(リストなど)にアクセスしようとしており、各スレッドをコードの任意の時点でランダムに停止できる場合、2番目のスレッドの変更が最初のスレッドによる変更を妨げる可能性があります。(参照:競合状態デッドロック。)

また、スレッドはオーバーヘッドが大きいため、「重い」と見なされます。これは、スレッドの切り替え時にかなりの時間のペナルティが発生することを意味します。


コルーチン

スレッドとは異なり、コルーチンは完全に同期しており、任意の時点で実行できるコルーチンは1つだけです。さらに、コルーチンは、いつ譲るかを選択できるようになるため、便利なコード内のポイント(たとえば、ループサイクルの最後)で譲ることを選択できます。これには、競合状態やデッドロックなどの問題を回避するのがはるかに簡単であるだけでなく、コルーチンが互いに協力しやすくなるという利点があります。

しかし、これも大きな責任です。コルーチンが適切に生成されない場合、プロセッサ時間を大量に消費することになり、共有リソースを誤って変更するとバグを引き起こす可能性があります。

通常、コルーチンはコンテキストの切り替えを必要としないため、切り替えが迅速に行われ、非常に軽量です。


要約すれば:

糸:

  • 同期または非同期
  • OSによってもたらされる
  • ランダムに降伏
  • ヘビー

コルーチン:

  • 同期
  • 自己をもたらす
  • 選択による収穫
  • 軽量

スレッドとコルーチンの役割は非常に似ていますが、ジョブの実行方法が異なるため、それぞれが異なるタスクにより適しています。スレッドは、中断されることなく自分で何かを行うことに集中し、完了時にシグナルを送り返すことができるタスクに最適です。コルーチンは、多くの小さなステップで実行できるタスクや、データを協調的に処理する必要があるタスクに最適です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.