ジョブの作成と実行
Create and schedule a job.
読み終わるまでの所要時間 13 分最終更新 1ヶ月前
ジョブを作成し、正しく実行するには、以下を行う必要があります。
- ジョブの作成: インターフェースを実装します。
IJob - ジョブのスケジュール: ジョブで メソッドを呼び出します。
Schedule - ジョブの完了待ち: ジョブがすでに完了している場合は、すぐに戻ります。データにアクセスするにはは、ジョブで メソッドを呼び出すことができます。
Complete
ジョブの作成
Unity でジョブを作成するには、 インターフェースを実装します。 の実装を使用して、実行中の他のジョブと並列処理する 1 つのジョブをスケジュールすることができます。
IJobIJobジョブを作成する際に、他のメソッドがこのジョブを参照するために必要な を作成することもできます。
JobHandle**重要:**読み取り専用ではないデータや可変の静的データは、ジョブ内からのアクセスされる場合に保護されません。この種のデータへのアクセスはすべての安全システムを回避し、アプリケーションや Unity エディターをクラッシュさせる可能性があります。
Unity が実行されると、ジョブシステムはスケジュールされたジョブデータのコピーを作成し、複数のスレッドが同じデータを読み取り/書き込みできないようにします。ジョブが終了した後は、 に書き込まれたデータだけにアクセスできます。これは、ジョブが使用する のコピーと元の オブジェクトの両方が同じメモリを指すためです。詳細は、スレッドセーフタイプ に関するドキュメントを参照してください。
NativeContainerNativeContainerNativeContainerジョブシステムがジョブキューからジョブをピックアップするとき、1 つのスレッドで メソッドを 1 度実行します。通常、ジョブシステムはバックグラウンドスレッドでジョブを実行しますが、メインスレッドがアイドルになると、メインスレッドを選択することができます。このため、ジョブは 1 フレーム以内に完了するように設計する必要があります。
Executeジョブのスケジュール
ジョブをスケジュールするには、 を呼び出します。これにより、ジョブがジョブキューに入れられます。依存関係 がある場合は、それがすべて完了したら、ジョブシステムがジョブの実行を開始します。スケジュールされたジョブを中断することはできません。メインスレッドからのみ を呼び出すことができます。
ScheduleSchedule**ヒント:**ジョブには、 の代わりに使用できる メソッドがあります。このメソッドを使用して、メインスレッドでジョブをすぐに実行できます。これはデバッグの目的で使うことができます。
ScheduleRunジョブの完了
ScheduleJobHandleCompleteCompleteCompleteNativeContainerCompleteジョブの例
using UnityEngine;using Unity.Collections;using Unity.Jobs;// Job adding two floating point values togetherpublic struct MyJob : IJob{ public float a; public float b; public NativeArray<float> result; public void Execute() { result[0] = a + b; }}
以下の例では、 ジョブに基づいて、メインスレッドにジョブをスケジュールします。
MyJobusing UnityEngine;using Unity.Collections;using Unity.Jobs;public class MyScheduledJob : MonoBehaviour{ // Create a native array of a single float to store the result. Using a // NativeArray is the only way you can get the results of the job, whether // you're getting one value or an array of values. NativeArray<float> result; // Create a JobHandle for the job JobHandle handle; // Set up the job public struct MyJob : IJob { public float a; public float b; public NativeArray<float> result; public void Execute() { result[0] = a + b; } } // Update is called once per frame void Update() { // Set up the job data result = new NativeArray<float>(1, Allocator.TempJob); MyJob jobData = new MyJob { a = 10, b = 10, result = result }; // Schedule the job handle = jobData.Schedule(); } private void LateUpdate() { // Sometime later in the frame, wait for the job to complete before accessing the results. handle.Complete(); // All copies of the NativeArray point to the same memory, you can access the result in "your" copy of the NativeArray // float aPlusB = result[0]; // Free the memory allocated by the result array result.Dispose(); }}
Schedule と Completeのベストプラクティス
必要なデータが手に入ったらすぐにジョブで を呼び出し、結果が必要になるときまで を呼び出さないのがベストプラクティスです。
ScheduleComplete重要度の低いジョブを、より重要な仕事と競合しないフレームの一部にスケジュールします。
例えば、あるフレームの終わりと次のフレームの始まりの間にジョブが実行されていない間隔があり、1 フレームの遅延が許容できる場合、フレームの終わりにジョブをスケジュールし、その結果を次のフレームで使用することができます。あるいは、アプリケーションが他のジョブでその切り替わりの間隔を飽和させ、フレーム内のどこかに十分に利用されていない間隔がある場合、代わりにそこにジョブをスケジュールする方が効率的です。
プロファイラー を使って、Unity がどこでジョブの完了を待っているかを確認することもできます。メインスレッドの マーカーはこれを示します。このマーカーは、解決すべきデータ依存関係がどこかに存在していることを意味する場合があります。メインスレッドを強制的に待機させているデータの依存関係を追跡するには、 を探してください。
WaitForJobGroupIDJobHandle.Complete長時間続くジョブの使用を避ける
スレッドとは異なり、ジョブは実行を放棄しません。ジョブが開始されると、そのジョブワーカースレッドは、他のジョブを実行する前に、そのジョブを完了させようとします。そのため、完了までの時間がシステム内の他のジョブに比べて長いジョブを適用する代わりに、実行時間が長いジョブを複数の 相互に依存する 小さなジョブに分割するのがベストプラクティスです。
ジョブシステムは通常、ジョブ依存関係の複数のチェーンを実行します。そのため、実行時間が長いタスクを複数に分割すると、複数のジョブチェーンが進行する可能性があります。長時間実行ジョブがジョブシステムを埋め尽くすと、ワーカースレッドをすべて消費して、独立したジョブの実行をブロックする可能性があります。これにより、メインスレッドが明示的に待機している重要なジョブの完了時間が遅くなり、メインスレッドで本来は存在しないはずのストールが発生することがあります。
特に、実行時間が長い ジョブは、ジョブシステムに悪影響を与えます。なぜなら、これらのジョブタイプは意図的にジョブのバッチサイズに対して可能な限り多くのワーカースレッドで実行しようとするからです。長い並列ジョブを分割できない場合は、ジョブのスケジュール時にバッチサイズを大きくして、長く実行されるジョブをピックアップするワーカーの数を制限することを検討してください。
IJobParallelForMyParallelJob jobData = new MyParallelJob();jobData.Data = someData; jobData.Result = someArray; // Use half the available worker threads, clamped to a minimum of 1 worker threadconst int numBatches = Math.Max(1, JobsUtility.JobWorkerCount / 2); const int totalItems = someArray.Length;const int batchSize = totalItems / numBatches;// Schedule the job with one Execute per index in the results array and batchSize items per processing batchJobHandle handle = jobData.Schedule(result.Length, totalItems, batchSize);