Saya memiliki algoritma level generation yang komputasional berat. Karena itu, menyebutnya selalu menghasilkan pembekuan layar game. Bagaimana saya bisa menempatkan fungsi pada utas kedua saat game masih terus membuat layar pemuatan untuk menunjukkan bahwa game tidak beku?
unity
multithreading
DarkDestry
sumber
sumber
List
Tindakan untuk menyimpan fungsi yang ingin Anda panggil di utas utama. kunci dan salinAction
daftar dalamUpdate
fungsi ke daftar sementara, kosongkan daftar asli kemudian jalankanAction
kode diList
utas utama. Lihat UnityThread dari posting saya yang lain tentang cara melakukan ini. Misalnya, untuk memanggil fungsi di Thread utama,UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
Jawaban:
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, tetapiSystem.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)
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.
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 berlaluyield return new WaitForEndOfFrame()
untuk melanjutkan setelah GUI selesai renderyield return myRequest
di manamyRequest
adalah contoh WWW , untuk melanjutkan setelah data yang diminta selesai memuat dari web atau disk.yield return otherCoroutine
di manaotherCoroutine
adalah contoh coroutine , untuk melanjutkan setelahotherCoroutine
selesai. Ini sering digunakan dalam bentukyield return StartCoroutine(OtherCoroutineMethod())
untuk menghubungkan eksekusi ke coroutine baru yang dapat dengan sendirinya menghasilkan bila diinginkan.Secara eksperimental, melewatkan yang kedua
StartCoroutine
dan hanya menulisyield 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 gunakanreturn;
untuk memulai metode konvensional.sumber
Anda dapat memasukkan perhitungan berat Anda ke utas lain, tetapi API Unity tidak aman, Anda harus menjalankannya di utas utama.
Nah Anda bisa mencoba paket ini di Asset Store akan membantu Anda menggunakan threading lebih mudah. http://u3d.as/wQg Anda cukup menggunakan hanya satu baris kode untuk memulai utas dan menjalankan API Persatuan dengan aman.
sumber
Menggunakan Svelto.Tasks, Anda dapat mengembalikan hasil dari rutinitas multi-utas ke utas utama (dan karenanya fungsi kesatuan) dengan cukup mudah:
http://www.sebaslab.com/svelto-taskrunner-run-serial-and-parallel-asynchronous-tasks-in-unity3d/
sumber
@DMGregory menjelaskannya dengan sangat baik.
Baik threading maupun coroutine dapat digunakan. Sebelumnya untuk membongkar utas utama dan kemudian untuk mengembalikan kontrol ke utas utama. Mendorong pengangkatan berat untuk memisahkan benang tampaknya lebih masuk akal. Karena itu, antrian pekerjaan mungkin seperti apa yang Anda cari.
Ada contoh script JobQueue yang sangat bagus di Unity Wiki.
sumber