Dalam permainan saya, ada bidang tanah dengan bangunan (rumah, pusat sumber daya). Bangunan seperti rumah memiliki penyewa, kamar, add-on, dan lain-lain, dan ada beberapa nilai yang harus disimulasikan berdasarkan semua variabel ini.
Sekarang, saya ingin menggunakan AndEngine untuk hal-hal ujung depan, dan membuat utas lain untuk melakukan perhitungan simulasi (mungkin juga nanti termasuk AI dalam utas ini). Ini agar satu utas utuh tidak melakukan semua pekerjaan dan menyebabkan masalah seperti pemblokiran. Ini memperkenalkan masalah konkurensi dan ketergantungan .
Masalah mata uang adalah utas utama UI saya dan utas perhitungan keduanya perlu mengakses semua objek simulasi. Jadi saya harus membuatnya aman, tetapi saya tidak tahu bagaimana menyimpan dan menyusun objek simulasi untuk mengaktifkannya.
Masalah ketergantungan adalah bahwa untuk menghitung nilai, perhitungan saya bergantung pada nilai objek lain.
Apa cara terbaik untuk menghubungkan objek penyewa saya di gedung dengan perhitungan saya? Hard-code ke kelas penyewa? Apa cara yang baik untuk melakukan algoritma "toko" sehingga mudah diubah?
Cara malas yang sederhana adalah dengan memasukkan semuanya ke dalam kelas yang menampung semua objek, seperti bidang tanah (yang pada gilirannya memegang bangunan, dan lain-lain). Kelas ini juga akan menyimpan status permainan seperti teknologi yang tersedia untuk pengguna, kumpulan objek untuk hal-hal seperti sprite. Tapi ini cara yang malas dan berbahaya, benar?
Sunting: Saya sedang melihat Dependency Injection, tetapi seberapa baik itu mengatasi seperti kelas yang memegang objek lain? yaitu, tanah saya, dengan bangunan, yang memiliki penyewa dan sejumlah nilai lainnya. DI terlihat seperti rasa sakit di gelandangan dengan AndEngine juga.
sumber
Jawaban:
Masalah Anda pada dasarnya serial - Anda harus menyelesaikan pembaruan simulasi sebelum dapat membuatnya. Mengosongkan simulasi ke utas berbeda hanya berarti utas UI utama tidak melakukan apa - apa sementara utas simulasi dicentang (yang artinya diblokir).
"Praktik terbaik" yang umum diadakan untuk konkurensi bukanlah untuk menempatkan rendering Anda pada satu utas dan simulasi Anda pada yang lain, seperti yang Anda usulkan. Saya sangat merekomendasikan menentang pendekatan itu, sebenarnya. Kedua operasi secara alami terkait seri, dan sementara mereka bisa dipaksa, itu tidak optimal dan tidak skala .
Pendekatan yang lebih baik adalah membuat bagian-bagian dari pembaruan atau rendering bersamaan, tetapi biarkan memperbarui dan rendering sendiri serial. Jadi misalnya, jika Anda memiliki batas alami dalam simulasi Anda (misalnya, jika rumah tidak pernah saling mempengaruhi dalam simulasi Anda), Anda dapat mendorong semua rumah menjadi ember N rumah, dan memutar seikat benang yang masing-masing proses satu ember, dan biarkan utas bergabung sebelum langkah pembaruan selesai. Timbangan ini jauh lebih baik dan jauh lebih cocok untuk desain bersamaan.
Anda terlalu memikirkan sisa masalah:
Ketergantungan injeksi adalah herring merah di sini: semua injeksi ketergantungan benar-benar berarti bahwa Anda lulus ("menyuntikkan") dependensi dari antarmuka ke instance dari antarmuka itu, biasanya selama konstruksi.
Itu berarti jika Anda memiliki kelas yang memodelkan a
House
, yang perlu mengetahui hal-halCity
yang ada di dalamnya, makaHouse
konstruktornya akan terlihat seperti:Tidak ada yang spesial.
Menggunakan singleton tidak perlu (Anda sering melihatnya dilakukan di beberapa "DI framework" gila-gilaan yang terlalu rumit, seperti Caliburn yang dirancang untuk aplikasi GUI "perusahaan" - ini tidak menjadikannya solusi yang baik). Bahkan, memperkenalkan lajang sering merupakan antitesis dari manajemen ketergantungan yang baik. Mereka juga dapat menyebabkan masalah serius dengan kode multithreaded karena biasanya tidak dapat dibuat aman tanpa kunci - semakin banyak kunci yang harus Anda peroleh, semakin buruk masalah Anda yang cocok untuk ditangani secara paralel.
sumber
Solusi biasa untuk masalah konkurensi adalah isolasi data .
Isolasi berarti bahwa setiap utas memiliki data sendiri, dan tidak menyentuh data utas lainnya. Dengan cara ini tidak ada masalah dengan konkurensi ... tetapi kemudian kita memiliki masalah komunikasi. Bagaimana utas ini dapat bekerja bersama jika tidak berbagi data apa pun?
Ada dua pendekatan di sini.
Yang pertama adalah kekekalan . Struktur / variabel yang tidak dapat berubah adalah yang tidak pernah mengubah keadaannya. Pada awalnya, ini mungkin terdengar tidak berguna - bagaimana kita bisa menggunakan "variabel" yang tidak pernah berubah? Namun, kita dapat menukar variabel-variabel ini! Pertimbangkan contoh ini: misalkan Anda memiliki
Tenant
kelas dengan sekelompok bidang, yang diperlukan dalam keadaan konsisten. Jika Anda mengubahTenant
objek di utas A, dan pada saat yang sama mengamatinya dari utas B, utas B dapat melihat objek dalam keadaan tidak konsisten. Namun, jikaTenant
tidak dapat diubah, utas A tidak dapat mengubahnya. Sebaliknya, itu menciptakan yang baruTenant
objek dengan bidang yang diatur sesuai kebutuhan, dan menukar dengan yang lama. Swapping hanyalah perubahan ke satu referensi, yang mungkin atom, dan dengan demikian tidak ada cara untuk mengamati objek dalam keadaan tidak konsisten.Pendekatan kedua adalah olahpesan . Gagasan di baliknya adalah bahwa ketika semua data "dimiliki" oleh beberapa utas, kami dapat memberi tahu utas ini apa yang harus dilakukan dengan data. Setiap utas dalam arsitektur ini memiliki antrian pesan - daftar
Message
objek, dan pompa pesan - metode yang terus berjalan yang menghapus pesan dari antrian, menafsirkannya dan memanggil beberapa metode penangan. Misalnya, Anda mengetuk sebidang tanah, menandakan bahwa itu perlu dibeli. Thread UI tidak dapat mengubahPlot
objek secara langsung, karena itu milik thread logika (dan mungkin tidak berubah). Jadi UI thread membangunBuyMessage
objek sebagai gantinya, dan menambahkannya ke antrian logika thread. Logika utas, saat dijalankan, mengambil pesan dari antrian dan panggilanBuyPlot()
, mengekstraksi parameter dari objek pesan. Mungkin mengirim pesan kembali, misalnyaBuySuccessfulMessage
, menginstruksikan utas UI untuk memasang "Sekarang Anda memiliki lebih banyak tanah!" jendela di layar. Tentu saja, akses ke antrian pesan harus disinkronkan dengan kunci, bagian kritis atau apa pun namanya di AndEngine. Tapi ini adalah satu titik sinkronisasi antara utas, dan utas ditangguhkan untuk waktu yang sangat singkat, jadi itu bukan masalah.Kedua pendekatan ini paling baik digunakan dalam kombinasi. Utas Anda harus berkomunikasi dengan pesan, dan memiliki beberapa data abadi "terbuka" untuk utas lainnya - misalnya, daftar plot yang tidak dapat diubah untuk ditarik oleh UI.
Perhatikan juga bahwa "hanya-baca" tidak selalu berarti tidak berubah ! Setiap struktur data yang kompleks seperti hashtable dapat mengubah status internalnya pada akses baca, jadi tanyakan pada dokumentasi terlebih dahulu.
sumber
Mungkin 99% dari program komputer yang ditulis dalam sejarah hanya menggunakan 1 utas dan berfungsi dengan baik. Saya tidak memiliki pengalaman tentang AndEngine tetapi sangat jarang menemukan sistem yang memerlukan threading, hanya beberapa yang dapat memperoleh manfaat darinya, mengingat perangkat keras yang tepat.
Secara tradisional, untuk melakukan simulasi dan GUI / rendering dalam satu utas, Anda cukup melakukan sedikit simulasi, lalu Anda render, dan Anda ulangi, biasanya berkali-kali per detik.
Ketika seseorang memiliki sedikit pengalaman dalam menggunakan beberapa proses, atau tidak sepenuhnya menghargai apa arti 'keselamatan' utas (yang merupakan istilah samar yang dapat berarti banyak hal yang berbeda), terlalu mudah untuk memperkenalkan banyak bug ke dalam suatu sistem. Jadi secara pribadi saya akan merekomendasikan mengambil pendekatan single-threaded, simulasi interleaving dan rendering, dan menyimpan segala threading untuk operasi yang Anda tahu pasti akan memakan waktu lama dan benar-benar membutuhkan thread dan bukan model berbasis peristiwa.
sumber