Baru-baru ini saya telah mengerjakan proyek yang banyak menggunakan threading. Saya pikir saya baik-baik saja dalam mendesainnya; gunakan desain stateless sebanyak mungkin, kunci akses ke semua sumber daya yang dibutuhkan lebih dari satu utas, dll. Pengalaman saya dalam pemrograman fungsional telah sangat membantu.
Namun, ketika membaca kode utas orang lain, saya bingung. Saya sedang debugging kebuntuan sekarang, dan karena gaya dan desain pengkodean berbeda dari gaya pribadi saya, saya mengalami kesulitan melihat kondisi kebuntuan potensial.
Apa yang Anda cari saat men-debug deadlock?
debugging
multithreading
Michael K.
sumber
sumber
Jawaban:
Jika situasinya benar-benar jalan buntu (yaitu dua utas memegang dua kunci berbeda, tetapi setidaknya satu utas menginginkan kunci yang dimiliki oleh utas lainnya) maka Anda harus terlebih dahulu meninggalkan semua pra-konsepsi tentang bagaimana utas memesan penguncian. Asumsikan tidak ada. Anda mungkin ingin menghapus semua komentar dari kode yang Anda lihat, karena komentar tersebut dapat membuat Anda percaya sesuatu yang tidak benar. Sulit untuk cukup menekankan hal ini: tidak menganggap apa-apa.
Setelah itu, tentukan kunci apa yang ditahan sementara utas mencoba untuk mengunci sesuatu yang lain. Jika Anda bisa, pastikan utas membuka dengan urutan terbalik dari penguncian. Lebih baik lagi, pastikan bahwa utas hanya memiliki satu kunci pada satu waktu.
Dengan susah payah bekerja melalui eksekusi utas, dan periksa semua acara penguncian. Di setiap kunci, tentukan apakah utas memegang kunci lain, dan jika demikian, dalam keadaan apa utas lainnya, yang melakukan jalur eksekusi yang sama, dapat sampai ke acara penguncian yang sedang dipertimbangkan.
Mungkin saja Anda tidak akan menemukan masalah sebelum kehabisan waktu atau uang.
sumber
Seperti yang dikatakan orang lain ... jika Anda bisa mendapatkan informasi yang berguna untuk login maka cobalah dulu karena itu adalah hal termudah untuk dilakukan.
Identifikasi kunci yang terlibat. Ubah semua mutex / semaphore yang menunggu selamanya untuk menunggu waktunya ... sesuatu yang sangat panjang seperti 5 menit. Catat kesalahan saat habis. Setidaknya ini akan mengarahkan Anda ke arah salah satu kunci yang terlibat dalam masalah ini. Tergantung variabilitas waktunya Anda mungkin beruntung dan menemukan kedua kunci setelah beberapa kali berjalan. Gunakan kode / kondisi kegagalan fungsi untuk mencatat jejak tumpukan semu setelah menunggu waktunya gagal mengidentifikasi bagaimana Anda sampai di sana di tempat pertama. Ini akan membantu Anda mengidentifikasi utas yang terlibat dalam masalah ini.
Hal lain yang dapat Anda coba adalah membangun perpustakaan pembungkus di sekitar layanan mutex / semaphore Anda. Lacak utas apa yang memiliki masing-masing mutex dan utas apa yang menunggu di mutex. Buat utas monitor yang memeriksa berapa lama utas yang telah diblokir. Pemicu pada durasi yang masuk akal dan buang informasi negara yang Anda lacak.
Pada titik tertentu, inspeksi kode lama biasa akan diperlukan.
sumber
Langkah pertama (seperti kata Péter) adalah mencatat. Meskipun dalam pengalaman saya ini sering bermasalah. Dalam pemrosesan paralel yang berat ini seringkali tidak mungkin. Saya harus men-debug sesuatu yang mirip dengan jaringan saraf sekali, yang memproses 100rb node per detik. Kesalahan terjadi hanya setelah beberapa jam dan bahkan satu baris output memperlambat banyak hal, sehingga butuh waktu berhari-hari. Jika logging memungkinkan, kurang berkonsentrasi pada data, tetapi lebih pada aliran program, sampai Anda tahu di bagian mana itu terjadi. Hanya garis sederhana di awal setiap fungsi dan jika Anda dapat menemukan fungsi yang tepat, bagi menjadi beberapa bagian yang lebih kecil.
Pilihan lain adalah menghapus bagian dari kode dan data untuk melokalisasi bug. Mungkin bahkan menulis beberapa program kecil yang hanya mengambil beberapa kelas dan hanya menjalankan tes paling dasar (masih dalam beberapa utas tentu saja). Hapus semua yang berhubungan dengan gui, misalnya output apa pun tentang status pemrosesan aktual. (Saya menemukan antarmuka pengguna cukup sering menjadi sumber bug)
Dalam kode Anda, coba ikuti alur kontrol logis yang lengkap antara menginisialisasi kunci dan melepaskannya. Kesalahan umum adalah mengunci di awal fungsi, membuka kunci di akhir, tetapi memiliki pernyataan pengembalian bersyarat di antara keduanya. Pengecualian juga bisa mencegah pembebasan.
sumber
Teman baik saya telah mencetak / mencatat pernyataan di tempat-tempat menarik dalam kode. Ini biasanya membantu saya memahami lebih baik apa yang sebenarnya terjadi di dalam aplikasi, tanpa mengganggu pengaturan waktu di antara berbagai utas, yang dapat mencegah mereproduksi bug.
Jika itu gagal, satu-satunya metode saya yang tersisa adalah menatap kode dan mencoba membangun model mental dari berbagai utas dan interaksi, dan mencoba memikirkan cara-cara gila yang mungkin untuk mencapai apa yang tampaknya telah terjadi :-) Tapi saya tidak menganggap diri saya seorang pemecah kebuntuan yang sangat berpengalaman. Semoga orang lain bisa memberikan ide yang lebih baik, dari mana saya bisa belajar juga :-)
sumber
Pertama-tama, cobalah untuk mendapatkan pembuat kode itu. Dia mungkin akan memiliki gagasan tentang apa yang telah ditulisnya. bahkan jika Anda berdua tidak dapat menentukan masalah hanya dengan berbicara, Setidaknya Anda bisa duduk dengannya untuk menentukan bagian kebuntuan, yang akan jauh lebih cepat daripada Anda memahami kode-kodenya tanpa bantuan.
Kegagalan itu, seperti kata Péter Török, Penebangan mungkin menjadi jalannya. Sejauh yang saya tahu, Debugger melakukan pekerjaan yang buruk pada lingkungan multi-threading. cobalah untuk menemukan di mana kuncinya, dapatkan sumber daya apa yang sedang menunggu, dan dalam kondisi apa kondisi balap terjadi.
sumber
Pertanyaan ini menarik saya;) Pertama-tama, anggap diri Anda beruntung karena Anda dapat mereproduksi masalah secara konsisten di setiap perjalanan. Jika Anda menerima pengecualian yang sama dengan stacktrace yang sama setiap kali maka itu harus cukup mudah. Jika tidak, maka jangan terlalu mempercayai stacktrace, alih-alih pantau saja akses ke objek global dan kondisinya berubah selama eksekusi.
sumber
Jika Anda harus men-debug deadlock, Anda sudah dalam masalah. Sebagai aturan, gunakan kunci untuk waktu sesingkat mungkin - atau tidak sama sekali, jika mungkin. Situasi apa pun di mana Anda mengambil kunci dan kemudian pergi ke kode non-sepele harus dihindari.
Tentu saja ini tergantung pada lingkungan pemrograman Anda, tetapi Anda harus melihat hal-hal seperti antrian berurutan yang memungkinkan Anda untuk mengakses sumber daya hanya dari satu utas.
Dan kemudian ada strategi lama tetapi sempurna: Menetapkan "level" untuk setiap kunci, mulai dari level 0. Jika Anda mengambil kunci level 0 Anda tidak diizinkan kunci lainnya. Setelah mengambil kunci level 1 Anda dapat mengambil kunci level 0. Setelah mengambil kunci level 10, Anda dapat mengambil kunci di level 9 atau lebih rendah dll.
Jika Anda menemukan ini tidak mungkin dilakukan, Anda harus memperbaiki kode Anda karena Anda akan mengalami kebuntuan.
sumber