NativeContainer は値型です。つまり、変数に代入されると、Unity は NativeContainer
構造体をコピーします。この構造体には、AtomicSafetyHandle
など、ネイティブコンテナのデータが保存されている場所へのポインターが保存されますが、NativeContainer
のコンテンツ全体をコピーするわけではありません。
このシナリオは、NativeContainer
構造体の複数のコピーが存在し、そのすべてが同じメモリ領域を参照し、すべてが同じ主要記録を参照する AtomicSafetyHandle
オブジェクトを含む可能性があることを意味します。
上図は、同じ実在するコンテナを表す NativeArray
構造体の 3 つの異なるコピーを示しています。各コピーは保存された同じデータを指しており、元の NativeArray
と同じ安全データを指しています。ただし、NativeArray
の各コピーには、ジョブがそのコピーで実行可能なことを示す異なるフラグがあります。安全データへのポインターは、フラグとの組み合わせで、AtomicSafetyHandle
を構成します。
NativeContainer
が破棄されると、NativeContainer
構造体のすべてのコピーは、元の NativeContainer
が無効であることを認識します。元の NativeContainer
の廃棄は、NativeContainer
のデータを保持していたメモリブロックが解放されたということです。この状況で NativeContainer
の各コピーに格納されているデータへのポインターは無効であり、これを使用するとアクセス違反を引き起こす可能性があります。
AtomicSafetyHandle
は、NativeContainer
インスタンスに対して無効となる主要記録も指します。ただし、安全システムは主要記録のメモリを解放しません。そのため、アクセス違反のリスクを回避できます。
代わりに、各記録にはバージョン番号が含まれます。バージョン番号のコピーは、そのレコードを参照する各 AtomicSafetyHandle
内に格納されます。NativeContainer が破棄されると、Unity は Release()
を呼び出し、主要記録のバージョン番号を増加します。この後、この記録を他の NativeContainer
インスタンスに再利用できます。
残存する各 AtomicSafetyHandle
は、NativeContainer
が破棄されたかどうかをテストするために、保存されているバージョン番号と主要記録のバージョン番号を比較します。Unity はこのテストを CheckReadAndThrow
や CheckWriteAndThrow
などのメソッドの呼び出しの一部として自動的に実行します。
動的なネイティブコンテナとは、NativeList<T>
(Collections パッケージで利用可能) など、要素を加え続けることができる可変のサイズを持つコンテナのことです。 のような静的ネイティブコンテナとは対照的です。 これは、NativeArray<T>
のようにサイズが変更不可能で固定されているものとは対照的です。
動的なネイティブコンテナを使用する場合、ビューと呼ばれる別のイン ターフェースを使用してそのデータに直接アクセスすることもできます。ビューを使用すると、NativeContainer
オブジェクトのデータをコピーしたり所有権を取得したりすることなく、エイリアスを作成できます。ビューの例としては、列挙オブジェクトがあり、これを使用してネイティブコンテナの要素ごとにデータにアクセスできます。また、 NativeList<T>.AsArray
などのメソッドは、NativeList
を NativeArray
のように扱うことができます。
動的なネイティブコンテナのサイズが変更される場合、ビューは通常スレッドセーフではありません。これは、ネイティブコンテナのサイズが変更されると、Unity がメモリ内のデータの格納場所を変更するため、ビューが格納するポインターが無効になるためです。
動的ネイティブコンテナのサイズが変更される場合に対応するため、安全システムには AtomicSafetyHandle
に補助バージョン番号が含まれています。このメカニズムはバージョンメカニズムに似ていますが、主要記録に格納された 2 番目のバージョン番号を使用し、1 番目のバージョン番号とは独立して増加されます。
補助バージョン番号を使用するには UseSecondaryVersion
を使用して、NativeContainer
に格納されているデータにビューを構成します。ネイティブコンテナのサイズを変更する操作や、既存のビューを無効にする操作には CheckWriteAndThrow
の代わりに CheckWriteAndBumpSecondaryVersion
を使用します。また、
NativeContainerに [
SetBumpSecondaryVersionOnScheduleWrite`](../ScriptReference/Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite.html) を設定し、ネイティブコンテナに書き込むジョブがスケジュールされるたびにビューを自動的に無効にする必要があります。
ビューを作成し、そこに AtomicSafetyHandle
をコピーする際に、CheckGetSecondaryDataPointerAndThrow
を使用して、ネイティブコンテナのメモリへのポインターをビューにコピーしても安全であることを確認します。
一時的なネイティブコンテナで作業するときに使える特別なハンドルが 2 つあります。
GetTempMemoryHandle
: Allocator.Temp
と共に割り当てられたネイティブコンテナで使用できる AtomicSafetyHandle
を返します。Unity は、現在の一時メモリスコープが終了すると、このハンドルを自動的に無効にします。そのため、自分で解除する必要はありません。特定の AtomicSafetyHandle
が GetTempMemoryHandle
によって返されたハンドルであるかどうかをテストするには、IsTempMemoryHandle
を使用します。GetTempUnsafePtrSliceHandle
: 安全でないメモリで保持された一時的なネイティブコンテナに使用できるグローバルハンドルを返します。例えば、スタックメモリから構築された NativeSlice
などです。このハンドルを使用するコンテナをジョブに渡すことはできません。