Pembaruan: Pada tahun 2018, Unity meluncurkan C # Job System sebagai cara untuk membongkar pekerjaan dan memanfaatkan beberapa inti CPU.
Jawaban di bawah ini ada sebelum sistem ini. Itu masih akan berfungsi, tetapi mungkin ada opsi yang lebih baik tersedia di Unity modern, tergantung pada kebutuhan Anda. Secara khusus, sistem pekerjaan tampaknya menyelesaikan beberapa batasan pada apa yang dapat diakses secara manual oleh thread yang dibuat secara manual, dijelaskan di bawah ini. Pengembang bereksperimen dengan laporan pratinjau melakukan raycast dan membangun jerat secara paralel , misalnya.
Saya akan mengundang pengguna dengan pengalaman menggunakan sistem pekerjaan ini untuk menambahkan jawaban mereka sendiri yang mencerminkan kondisi mesin saat ini.
Saya telah menggunakan threading untuk tugas-tugas kelas berat di Unity di masa lalu (biasanya pemrosesan gambar & geometri), dan itu tidak jauh berbeda dari menggunakan utas dalam aplikasi C # lainnya, dengan dua peringatan:
Karena Unity menggunakan subset yang agak lebih lama dari .NET, ada beberapa fitur threading yang lebih baru dan pustaka yang tidak dapat kita gunakan di luar kotak, tetapi semua dasar-dasarnya ada di sana.
Seperti yang dicatat oleh Almo dalam komentar di atas, banyak tipe Unity bukan threadsafe, dan akan mengeluarkan pengecualian jika Anda mencoba membuat, menggunakan, atau bahkan membandingkannya dari utas utama. Hal-hal yang perlu diingat:
Satu kasus umum adalah memeriksa untuk melihat apakah referensi GameObject atau Monobehaviour adalah nol sebelum mencoba mengakses anggotanya. myUnityObject == null
panggilan operator kelebihan beban untuk apa pun yang diturunkan dari UnityEngine.Object, tetapi System.Object.ReferenceEquals()
bekerja di sekitar ini ke tingkat - hanya ingat bahwa Hancurkan () ed GameObject membandingkan sama dengan nol menggunakan kelebihan beban, tetapi belum ReferenceEqual ke nol.
Membaca parameter dari jenis Unity biasanya aman di thread lain (dalam hal itu tidak akan segera melemparkan sebuah pengecualian selama Anda sedang berhati-hati untuk memeriksa nulls seperti di atas), tetapi catatan peringatan Philipp sini bahwa thread utama mungkin memodifikasi negara saat Anda membacanya. Anda harus disiplin tentang siapa yang diizinkan untuk mengubah apa & kapan untuk menghindari membaca keadaan yang tidak konsisten, yang dapat menyebabkan bug yang sangat sulit untuk dilacak karena mereka bergantung pada waktu sub-milidetik di antara utas yang kami dapat dapat mereproduksi sesuka hati.
Anggota statis acak dan Waktu tidak tersedia. Buat instance dari System.Random per utas jika Anda memerlukan keacakan, dan System.Diagnostics.Stopwatch jika Anda memerlukan info waktu.
Fungsi Mathf, Vector, Matrix, Quaternion, dan struct Warna semua berfungsi dengan baik di seluruh utas, sehingga Anda dapat melakukan sebagian besar perhitungan secara terpisah
Membuat GameObjects, melampirkan Monobehaviours, atau membuat / memperbarui Tekstur, Jerat, Bahan, dll. Semua harus terjadi pada utas utama. Di masa lalu ketika saya harus bekerja dengan ini, saya telah membuat antrian produsen-konsumen, di mana utas pekerja saya menyiapkan data mentah (seperti array besar vektor / warna untuk diterapkan pada mesh atau tekstur), dan Pembaruan atau Coroutine pada jajak pendapat utas utama untuk data dan menerapkannya.
Dengan catatan itu, inilah pola yang sering saya gunakan untuk pekerjaan berulir. Saya tidak membuat jaminan bahwa itu adalah gaya praktik terbaik, tetapi itu menyelesaikan pekerjaan. (Komentar atau suntingan untuk meningkatkan dipersilahkan - Saya tahu threading adalah topik yang sangat mendalam yang saya hanya tahu dasar-dasarnya)
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.
}
}
Jika Anda tidak benar-benar perlu membagi pekerjaan di thread untuk kecepatan, dan Anda hanya mencari cara untuk membuatnya non-blocking sehingga sisa gim Anda terus berdetak, solusi yang lebih ringan di Unity adalah Coroutine . Ini adalah fungsi yang dapat melakukan beberapa pekerjaan kemudian menghasilkan kontrol kembali ke mesin untuk melanjutkan apa yang dilakukannya, dan melanjutkan dengan mulus di lain waktu.
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...
}
}
}
Ini tidak memerlukan pertimbangan pembersihan khusus, karena mesin (sejauh yang saya tahu) menghilangkan coroutine dari benda yang hancur untuk Anda.
Semua keadaan lokal dari metode ini dipertahankan ketika menghasilkan dan melanjutkan, jadi untuk banyak tujuan seolah-olah itu berjalan tanpa gangguan pada utas lainnya (tetapi Anda memiliki semua kemudahan menjalankan pada utas utama). Anda hanya perlu memastikan setiap iterasi itu cukup pendek sehingga tidak akan memperlambat utas Anda secara tidak masuk akal.
Dengan memastikan operasi penting tidak dipisahkan oleh hasil, Anda bisa mendapatkan konsistensi perilaku single-threaded - mengetahui bahwa tidak ada skrip atau sistem lain di utas utama yang dapat mengubah data yang sedang Anda kerjakan.
Garis pengembalian hasil memberi Anda beberapa opsi. Kamu bisa...
yield return null
untuk melanjutkan setelah pembaruan frame berikutnya ()
yield return new WaitForFixedUpdate()
untuk melanjutkan setelah FixedUpdate berikutnya ()
yield return new WaitForSeconds(delay)
untuk melanjutkan setelah sejumlah waktu permainan berlalu
yield return new WaitForEndOfFrame()
untuk melanjutkan setelah GUI selesai render
yield return myRequest
di mana myRequest
adalah contoh WWW , untuk melanjutkan setelah data yang diminta selesai memuat dari web atau disk.
yield return otherCoroutine
di mana otherCoroutine
adalah contoh coroutine , untuk melanjutkan setelah otherCoroutine
selesai. Ini sering digunakan dalam bentuk yield return StartCoroutine(OtherCoroutineMethod())
untuk menghubungkan eksekusi ke coroutine baru yang dapat dengan sendirinya menghasilkan bila diinginkan.
Secara eksperimental, melewatkan yang kedua StartCoroutine
dan hanya menulis yield return OtherCoroutineMethod()
mencapai tujuan yang sama jika Anda ingin rantai eksekusi dalam konteks yang sama.
Membungkus di dalam StartCoroutine
mungkin masih berguna jika Anda ingin menjalankan coroutine bersarang dalam kaitannya dengan objek kedua, sepertiyield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())
... tergantung kapan Anda ingin coroutine mengambil giliran berikutnya.
Atau yield break;
untuk menghentikan coroutine sebelum mencapai akhir, cara yang mungkin Anda gunakan return;
untuk memulai metode konvensional.