データをコピーする 安全システム による処理の欠点は、各コピー内のジョブの結果も単独化してしまうことです。この制限を解決するには、NativeContainer と呼ばれる共有メモリに結果を保存する必要があります。
NativeContainer
はマネージ型で、比較的安全な C# ラッパーをネイティブメモリに提供します。これには、アンマネージの割り当てへのポインターが含まれます。Unity C# Job System で使用する場合、NativeContainer
を使用すると、ジョブはコピーを使って作業するのではなく、メインスレッドと共有するデータにアクセスできるようになります。
Unity には、NativeArray と呼ばれる NativeContainer
が搭載されています。NativeArray
を NativeSlice で操作して、NativeArray
の特定の位置から特定の長さのサブセットを取得することもできます。
注意 Entity Component System (ECS) パッケージは、Unity.Collections
名前空間を拡張して、他の NativeContainer
属性のある型が含まれています。
NativeList
- サイズ変更可能な NativeArray
NativeHashMap
- キーと値のペアNativeMultiHashMap
- 各キーに複数の値NativeQueue
- 先入れ先出し (FIFO) キューセーフティシステムはすべての NativeContainer
タイプに組み込まれています。 NativeContainer
へのすべての読み取りと書き込みを追跡します。
注意 NativeContainer
タイプのすべての安全チェック (例えば、範囲外チェック、割り当て解除チェック、競合状態チェック) は、Unity の エディター と 再生モード でのみ使用可能です。
この安全システムには DisposeSentinel と AtomicSafetyHandle が含まれます。 DisposeSentinel
はメモリリークを検出し、正しくメモリが解放されていない場合はエラーを返します。メモリリークエラーは、リークが発生してからかなり後に送信されます。
AtomicSafetyHandle
を使用すると、コードの NativeContainer
の所有権を移動します。例えば、2 つのスケジュールされたジョブが同じ NativeArray
に書き込みを行なうと、安全システムは例外を発生させ、問題の原因と解決方法を説明する明確なエラーメッセージを示します。安全システムは、違反のジョブがスケジュールされたときにこの例外をスローします。
この場合、依存関係を持つジョブをスケジュールに組み込めます。最初のジョブは NativeContainer
に書き込むことができ、実行が終了すると、次のジョブは同じ NativeContainer
から安全に読み取り、または書き込みすることができます。読み取りと書き込みの制限は、メインスレッドからデータにアクセスするときにも適用されます。安全システムでは、複数のジョブが並列で同じデータから読み取ることができます。
デフォルトでは、NativeContainer
へアクセスできるジョブは、読み取りと書き込みの両方のアクセスが可能です。この設定はパフォーマンスを遅くする可能性があります。C# Job System では、NativeContainer
への書き込みアクセス権を持つジョブを、NativeContainer
への書き込みアクセス権を持つ他のジョブと同時にスケジュールすることはできません。
ジョブが NativeContainer
への書き込みを必要としない場合は、以下のように NativeContainer
に [ReadOnly]
属性を設定します。
[ReadOnly]
public NativeArray<int> input;
上の例では、読み取り専用アクセス権を持つジョブと、NativeContainer
への読み取り専用アクセス権を持つその他のジョブを同時に実行することができます。
注意 ジョブ内からの静的データへのアクセスに対する保護はありません。静的データにアクセスすると、すべての安全システムを回避し、Unity をクラッシュさせる可能性があります。詳細は、C# Job System のヒントとトラブルシューティング を参照してください。
NativeContainer
を作成するときに、必要なメモリ割り当てのタイプを指定する必要があります。割り当てタイプは、ジョブが実行される時間の長さによって異なります。これにより、各状況で最高のパフォーマンスが得られるように割り当てを調整できます。
NativeContainer
メモリ割り当てと解放には 3 つの Allocator タイプがあります。NativeContainer
をインスタンス化するときに適切なものを指定する必要があります。
Temp
を使って割り当てを行った NativeContainer
をジョブに渡すべきではありません。また、メソッドの呼び出し (MonoBehaviour.Update など) や、その他すべてのネイティブコードからマネージコードへのコールバックから戻る前に Dispose
メソッドを呼び出す必要があります。Temp
よりもスピードの遅い割り当てですが、Persistent
よりも高速です。これは、4 フレーム以内のスパンの割り当てであり、スレッドセーフです。4 フレーム以内で Dispose
を行わないと、コンソールはネイティブコードから生成された警告を出力します。ほとんどの小さなジョブはこの割り当てタイプを使用します。Persistent
を使用しないでください。例
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);
ノート 上の例の数字 1 は、 NativeArray
のサイズを示しています。この場合、配列要素は 1 つだけです (result
に 1 つのデータしか格納していないため)。