1. 程式人生 > >C#Job System如何工作(5)-NativeContainer

C#Job System如何工作(5)-NativeContainer

NativeContainer

安全系統複製資料的過程的缺點是它還隔離了每個副本中Job的結果。要克服此限制,您需要將NativeContainer結果儲存在一種名為NativeContainer的共享記憶體中。

什麼是NativeContainer?

NativeContainer是託管值型別,為本機記憶體提供相對安全的C#包裝器。它包含指向非託管分配的指標。與Unity C#作業系統一起使用時,一個 NativeContainer允許Job訪問與主執行緒共享的資料,而不是使用拷貝副本資料。

有哪些型別的NativeContainer?

Unity附帶一個NativeContainer名為NativeArray的程式。您還可以使用NativeSlice操作一個NativeArray來獲取NativeArray從指定位置到指定長度的子集。

注意:實體元件系統(ECS)包擴充套件了Unity.Collections名稱空間以包括其他型別的NativeContainer:

  • NativeList- 可調整大小的NativeArray。
  • NativeHashMap - 鍵值對。
  • NativeMultiHashMap - 每個鍵有多個值。
  • NativeQueue- 先進先出(FIFO)佇列。

NativeContainer和安全系統

安全系統內置於所有NativeContainer型別。它跟蹤NativeContainer中正在閱讀和寫入的內容。

注意:所有NativeContainer型別的安全檢查(例如越界檢查,重新分配檢查和競爭條件檢查)僅在Unity Editor和Play模式下可用。

該安全系統的一部分是DisposeSentinel和AtomicSafetyHandle。該DisposeSentinel檢測記憶體洩漏,如果你沒有正確地釋放你的記憶體,就會報錯。記憶體洩漏發生後很久就會發生記憶體洩漏錯誤。 使用AtomicSafetyHandle轉移NativeContainer程式碼的所有權。例如,如果兩個排程Job寫入相同NativeArray,則安全系統會丟擲一個異常,並顯示一條明確的錯誤訊息,說明解決問題的原因和方法。當你排程違規Job時,安全系統會丟擲此異常。

在這種情況下,您可以排程具有依賴關係的Job。第一個Job可以寫入NativeContainer,一旦完成執行,下一個Job就可以安全地讀取和寫入上一個Job相同的NativeContainer。從主執行緒訪問資料時,讀寫限制也適用。安全系統允許多個Job並行讀取相同的資料。

預設情況下,當Job有權訪問一個NativeContainer時,它具有讀寫訪問許可權。此配置可能會降低效能。C#Job System不允許您在一個job正在寫入NativeContainer時同時排程另外一個對NativeContainer 有寫入許可權的Job。

如果作業不需要寫入一個 NativeContainer,請使用[ReadOnly]屬性標記NativeContainer,如下所示:

[ReadOnly]
public NativeArray<int> input;

在上面的示例中,您可以與其他對第一個也具有隻讀訪問許可權的作業同時執行作業NativeArray。

注意:無法防止從作業中訪問靜態資料。訪問靜態資料會繞過所有安全系統,並可能導致Unity崩潰。有關更多資訊,請參閱C#作業系統提示和故障排除。

NativeContainer分配器

當建立 NativeContainer時,必須指定所需的記憶體分配型別。分配型別取決於Job執行的時間長度。通過這種方式,您可以定製分配以在每種情況下獲得最佳效能。 NativeContainer記憶體分配和釋放有三種分配器型別。在例項化你的NativeContainer時候需要指定合適的一個型別。

  • Allocator.Temp分配的時候最快。它適用於壽命為一幀或更少的分配。您不應該使用Temp將NativeContainer分配傳遞給Jobs。您還需要在從方法(例如MonoBehaviour.Update,或從本機程式碼到託管程式碼的任何其他回撥)呼叫返回之前呼叫該方法Dispose()。
  • Allocator.TempJob是一個比Temp慢的分配,但速度比Persistent快。它適用於四幀生命週期內的分配,並且是執行緒安全的。如果在四個幀內沒有呼叫Dispose,則控制檯會列印一個從本機程式碼生成的警告。大多數小型Jobs都使用這個NativeContainer分配型別。
  • Allocator.Persistent是最慢的分配,只要你需要它,就一直存在。並且如果有必要的話,可以持續整個應用程式的生命週期。它是直接呼叫malloc的包裝器。較長的Jobs可以使用此NativeContainer分配型別。你不應該使用Persistent在效能至關重要的地方使用。

例如:

NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);

注意:上例中的數字1表示NativeArray的大小。在這種情況下,它只有一個數組元素(因為它只儲存一個數據result)。