อัปเดต:ในปี 2018 Unity กำลังเปิดตัวระบบงาน C #เพื่อลดการทำงานและใช้ประโยชน์จากคอร์ CPU หลายตัว
คำตอบด้านล่างนี้มาก่อนระบบนี้ มันจะยังคงใช้งานได้ แต่อาจมีตัวเลือกที่ดีกว่าใน Unity สมัยใหม่ขึ้นอยู่กับความต้องการของคุณ โดยเฉพาะอย่างยิ่งระบบงานจะปรากฏขึ้นเพื่อแก้ไขข้อ จำกัด บางอย่างเกี่ยวกับสิ่งที่หัวข้อที่สร้างขึ้นด้วยตนเองสามารถเข้าถึงได้อย่างปลอดภัยอธิบายไว้ด้านล่าง นักพัฒนาที่ทดลองใช้รายงานตัวอย่างแสดงเรย์แคสต์และสร้างตาข่ายแบบขนานตัวอย่างเช่น
ฉันขอเชิญผู้ใช้ที่มีประสบการณ์ในการใช้ระบบงานนี้เพื่อเพิ่มคำตอบของตัวเองที่สะท้อนสถานะปัจจุบันของเครื่องยนต์
ฉันใช้เธรดสำหรับงานหนาใน Unity ในอดีต (โดยปกติแล้วการประมวลผลภาพและเรขาคณิต) และก็ไม่แตกต่างอย่างมากจากการใช้เธรดในแอปพลิเคชัน C # อื่น ๆ โดยมีสองประการ:
เนื่องจาก Unity ใช้ชุดย่อยของ. NET ที่ค่อนข้างเก่ากว่ามีคุณลักษณะการทำเธรดและไลบรารีที่ใหม่กว่าที่เราไม่สามารถใช้งานได้ แต่พื้นฐานทั้งหมดอยู่ที่นั่น
เนื่องจาก Almo บันทึกไว้ในความคิดเห็นข้างต้น Unity หลายประเภทไม่ได้เป็น threadsafe และจะโยนข้อยกเว้นหากคุณพยายามสร้างใช้หรือเปรียบเทียบกับพวกมันนอกเธรดหลัก สิ่งที่ควรทราบ:
กรณีหนึ่งที่พบบ่อยคือการตรวจสอบเพื่อดูว่าการอ้างอิง GameObject หรือ Monobehaviour เป็นโมฆะก่อนที่จะพยายามเข้าถึงสมาชิก myUnityObject == null
เรียกใช้โอเปอเรเตอร์ที่โอเวอร์โหลดสำหรับสิ่งใดก็ตามที่สืบทอดมาจาก UnityEngineObject แต่ใช้System.Object.ReferenceEquals()
งานได้ในระดับหนึ่ง - เพียงจำไว้ว่าการทำลาย () ed GameObject จะเปรียบเทียบเท่ากับโมฆะโดยใช้โอเวอร์โหลด แต่ยังไม่ได้อ้างอิงกับคิว
การอ่านพารามิเตอร์จากประเภท Unity มักจะปลอดภัยในเธรดอื่น (ซึ่งจะไม่เกิดข้อยกเว้นทันทีหากคุณระมัดระวังในการตรวจสอบค่า null ดังกล่าวข้างต้น) แต่ให้สังเกตคำเตือนของ Philipp ที่นี่ว่าเธรดหลักอาจแก้ไขสถานะ ในขณะที่คุณกำลังอ่าน คุณจะต้องได้รับการลงโทษทางวินัยเกี่ยวกับผู้ที่ได้รับอนุญาตให้แก้ไขอะไรและเมื่อใดเพื่อหลีกเลี่ยงการอ่านสถานะที่ไม่สอดคล้องกันบางอย่างซึ่งอาจนำไปสู่ข้อบกพร่องที่ยากต่อการติดตามอย่างมากเนื่องจากขึ้นอยู่กับการกำหนดเวลา ทำซ้ำตามความประสงค์
สมาชิกสุ่มและเวลาคงไม่พร้อมใช้งาน สร้างอินสแตนซ์ของ System.Random ต่อเธรดหากคุณต้องการการสุ่มและ System.Diagnostics.Stopwatch ถ้าคุณต้องการข้อมูลเวลา
ฟังก์ชัน Mathf, Vector, Matrix, Quaternion และ Color structs ทั้งหมดทำงานได้ดีในหลาย ๆ เธรดดังนั้นคุณสามารถทำการคำนวณส่วนใหญ่แยกกัน
การสร้าง GameObjects, การแนบ Monobehaviours หรือการสร้าง / อัพเดท Textures, Meshes, Materials และอื่น ๆ ล้วนต้องเกิดขึ้นในเธรดหลัก ในอดีตเมื่อฉันต้องการทำงานกับสิ่งเหล่านี้ฉันได้สร้างคิวผู้ผลิตและผู้บริโภคที่พนักงานของฉันเตรียมข้อมูลดิบ (เช่นเวกเตอร์ / สีจำนวนมากเพื่อนำไปใช้กับตาข่ายหรือพื้นผิว) และอัปเดตหรือ Coroutine ในการสำรวจความคิดเห็นหลักสำหรับข้อมูลและนำไปใช้
นี่คือรูปแบบที่ฉันมักจะใช้สำหรับงานที่ทำเกลียว ฉันไม่รับประกันว่ามันจะเป็นแนวปฏิบัติที่ดีที่สุด แต่ก็ทำให้งานเสร็จได้ (ความเห็นหรือการแก้ไขเพื่อปรับปรุงยินดีต้อนรับ - ฉันรู้ว่าการทำเกลียวเป็นหัวข้อที่ลึกมากซึ่งฉันรู้พื้นฐานเท่านั้น)
using UnityEngine;
using System.Threading;
public class MyThreadedBehaviour : MonoBehaviour
{
bool _threadRunning;
Thread _thread;
void Start()
{
// Begin our heavy work on a new thread.
_thread = new Thread(ThreadedWork);
_thread.Start();
}
void ThreadedWork()
{
_threadRunning = true;
bool workDone = false;
// This pattern lets us interrupt the work at a safe point if neeeded.
while(_threadRunning && !workDone)
{
// Do Work...
}
_threadRunning = false;
}
void OnDisable()
{
// If the thread is still running, we should shut it down,
// otherwise it can prevent the game from exiting correctly.
if(_threadRunning)
{
// This forces the while loop in the ThreadedWork function to abort.
_threadRunning = false;
// This waits until the thread exits,
// ensuring any cleanup we do after this is safe.
_thread.Join();
}
// Thread is guaranteed no longer running. Do other cleanup tasks.
}
}
ถ้าคุณไม่จำเป็นต้องเคร่งครัดในการทำงานข้ามแยกหัวข้อสำหรับความเร็วและคุณเพียงแค่มองหาวิธีที่จะทำให้มันไม่ปิดกั้นเพื่อให้ส่วนที่เหลือของเกมของคุณช่วยให้ฟ้องเป็นทางออกที่มีน้ำหนักเบาในความสามัคคีเป็นCoroutines นี่คือฟังก์ชั่นที่สามารถทำงานได้บางอย่างจากนั้นควบคุมการกลับไปที่เครื่องยนต์เพื่อทำสิ่งที่มันทำต่อไปและกลับมาทำงานได้อย่างราบรื่นในเวลาต่อมา
using UnityEngine;
using System.Collections;
public class MyYieldingBehaviour : MonoBehaviour
{
void Start()
{
// Begin our heavy work in a coroutine.
StartCoroutine(YieldingWork());
}
IEnumerator YieldingWork()
{
bool workDone = false;
while(!workDone)
{
// Let the engine run for a frame.
yield return null;
// Do Work...
}
}
}
นี่ไม่จำเป็นต้องมีข้อควรพิจารณาในการทำความสะอาดเป็นพิเศษเนื่องจากเครื่องยนต์ (เท่าที่ฉันสามารถบอกได้) กำจัด coroutines จากวัตถุที่ถูกทำลายให้คุณ
สถานะโลคัลทั้งหมดของเมธอดจะถูกเก็บรักษาไว้เมื่อมันให้ผลตอบแทนและดำเนินการต่อดังนั้นเพื่อวัตถุประสงค์หลายอย่างราวกับว่ามันทำงานอย่างต่อเนื่องบนเธรดอื่น (แต่คุณมีความสะดวกทั้งหมดในการทำงานบนเธรดหลัก) คุณเพียงแค่ต้องแน่ใจว่าการวนซ้ำแต่ละครั้งนั้นสั้นพอที่จะไม่ทำให้เธรดหลักของคุณช้าลงอย่างไม่มีเหตุผล
โดยการทำให้มั่นใจว่าการดำเนินการที่สำคัญไม่ได้ถูกคั่นด้วยผลตอบแทนคุณสามารถได้รับความสอดคล้องของพฤติกรรมแบบเธรดเดียวโดยรู้ว่าไม่มีสคริปต์หรือระบบอื่นในเธรดหลักสามารถแก้ไขข้อมูลที่คุณกำลังทำงานอยู่
บรรทัดผลตอบแทนให้คุณเลือกได้สองสามทาง คุณสามารถ...
... ขึ้นอยู่กับว่าคุณต้องการให้ coroutine เลี้ยวต่อไปเมื่อไหร่
หรือyield break;
เพื่อหยุด coroutine ก่อนที่จะถึงจุดสิ้นสุดวิธีที่คุณอาจใช้return;
ในการเริ่มต้นของวิธีดั้งเดิม