Apa sebenarnya "Tidak Merespons" — cara Windows memutuskan aplikasi hang, dan cara merancang aplikasi yang tidak hang

· · Windows, Pengembangan Windows, Pemecahan masalah, Multithreading, WinForms, WPF, Win32 API, Desain UI

“Aplikasi menjadi putih di tengah operasi dan menampilkan (Tidak Merespons).” “Kami mendapat tiket bahwa ia hang kadang-kadang, tetapi tidak pernah tereproduksi di mesin pengembangan.” — Untuk aplikasi bisnis Windows, “Tidak Merespons” ini adalah salah satu keluhan paling umum. Dan yang mengejutkan sedikit diketahui adalah bahwa yang memasang tampilan “Tidak Merespons” bukan aplikasi yang hang itu sendiri — itu OS.

Bagaimana Windows tahu bahwa aplikasi telah “hang”? Apa jendela putih beku itu? Ditujukan kepada pengembang yang menulis aplikasi bisnis di Windows dan staf IT yang menerima tiket tentang aplikasi yang hang, artikel ini menelusuri penilaian “Tidak Merespons” dari dasar message loop, dan menata penyebab klasik hang, desain yang tidak hang, serta prosedur menyelidiki momen hang — semuanya berdasarkan sumber primer.

1. Kesimpulan lebih dulu

  • “Tidak Merespons” adalah penilaian OS. Ketika jendela (dan thread GUI yang memilikinya) tidak menunggu input, tidak dalam urutan start-nya, dan belum mengambil pesan (PeekMessage) selama 5 detik, OS memperlakukannya sebagai tidak merespons. Penilaiannya bukan per-proses.1
  • Jendela yang memutih adalah “jendela hantu”. OS telah menyembunyikan jendela asli dan menukar masuk palsu dengan posisi, ukuran, dan tampilan yang sama. Yang dapat Anda lakukan hanyalah memindahkan, meminimalkan, atau menutupnya; isinya tidak berjalan. Jendela hantu tidak dibuat sementara debugger terlampir.2
  • Penyebab hang hampir selalu turun ke satu hal. Thread UI yang seharusnya memompa message loop diblokir pada pekerjaan berat atau tunggu. I/O sinkron, panggilan jaringan, tunggu kunci, dan SendMessage lintas thread adalah yang klasik.3
  • Prinsip desain adalah “jangan menunggu atau menghitung di thread UI”. Pindahkan pekerjaan berat ke thread worker (async/await + Task.Run di C#) dan biarkan thread UI dikhususkan untuk menggambar, kemajuan, dan menerima pembatalan.4
  • DoEvents dan memompa message loop secara manual adalah tempat berkembang biaknya bug reentrancy. Tampilan “Tidak Merespons” hilang, tetapi strukturnya sekarang membiarkan event sembarang menginterupsi di tengah pekerjaan. Pemisahan, bukan pengelakan, adalah pendekatan yang tepat.
  • Investigasi dimulai dengan menangkap state pada momen hang. Ambil dump dan lihat stack thread UI, dan Anda hampir selalu dapat mengidentifikasi apa yang ditunggunya.

2. Prasyarat: aplikasi Windows didorong oleh pesan

Untuk memahami “Tidak Merespons”, Anda pertama-tama perlu menyerap bahwa aplikasi GUI Windows adalah event-driven. Aplikasi GUI tidak pergi mengambil input sendiri; ia menerima pesan yang OS kirimkan (mouse, keyboard, permintaan gambar ulang, timer, dan sebagainya) dan bertindak atasnya.3

Setiap thread yang membuat jendela punya antrian pesan dan menjalankan message loop seperti ini.

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);   // the window procedure is called
}

GetMessage mengambil pesan dari antrian, dan DispatchMessage memanggil prosedur jendela jendela itu (fungsi penanganan pesan). Penanganan klik tombol, menggambar ulang, dan event handler WinForms atau WPF semuanya, ketika Anda merebusnya, berjalan di dalam satu iterasi loop ini.5

Struktur dasar message loopOS menempatkan mouse, keyboard, dan input lain ke antrian pesan thread; loop thread UI mengambilnya dengan GetMessage, memanggil prosedur jendela dengan DispatchMessage, dan kembali ke puncak loop ketika pemrosesan selesaiOS (input, permintaan gambar ulang, timer)Antrian pesan threadAmbil dengan GetMessageDispatchMessageTangani di prosedur jendela

Gambar 1: Jantung aplikasi GUI adalah message loop; setiap event handler berjalan sebagai satu iterasi loop ini.

Struktur ini punya satu konsekuensi penting. Jika Anda melakukan pekerjaan yang memakan waktu di dalam prosedur jendela (event handler), loop tidak dapat mengambil pesan berikutnya sementara itu. Ia tidak dapat bereaksi baik terhadap klik maupun permintaan gambar ulang — itulah yang sebenarnya “hang”.

Juga perlu diserap bahwa pesan dikirim lewat dua jalur. PostMessage menempatkan pesan pada antrian dan segera kembali, dan loop mengambil serta memproses pesan secara berurutan. SendMessage, di sisi lain, memanggil prosedur jendela secara langsung dan tidak kembali ke pemanggil sampai pemrosesan selesai.36 Perbedaan itu langsung masuk ke pembahasan deadlock di Bab 4.

Dua jalur pengiriman pesanPostMessage menempatkan pesan pada antrian dan segera kembali; message loop mengambil dan memproses pesan secara berurutan. SendMessage memanggil prosedur jendela secara langsung dan tidak kembali ke pemanggil sampai pemrosesan selesaiPostMessageTempatkan pada antrian (segera kembali)Loop mengambil dan memproses secara berurutanSendMessagePanggil prosedur secara langsungTidak kembali sampai pemrosesan selesai

Gambar 2: Meskipun keduanya “mengirim pesan”, Post yang diantrekan dan Send yang menunggu penyelesaian punya sifat yang sama sekali berbeda.

3. Bagaimana “Tidak Merespons” dinilai — aturan 5 detik dan jendela hantu

Jadi bagaimana OS tahu bahwa “aplikasi ini telah hang”? Kriterianya didokumentasikan secara resmi. OS memperlakukan jendela sebagai tidak merespons ketika ia tidak menunggu input, tidak dalam urutan start-nya, dan belum memanggil PeekMessage (pengambilan pesan) selama 5 detik.1 Dengan kata lain, OS mengawasi apakah “message loop benar-benar berputar” seperti Anda mengambil denyut, dan jika tidak ada denyut selama 5 detik ia menilai jendela tidak merespons (dokumentasi menyatakan bahwa nilai 5 detik ini dapat berubah di masa depan). Satuan penilaian adalah jendela dan thread GUI yang memilikinya; di aplikasi dengan beberapa thread UI, satu thread hang tidak berarti jendela di thread lain mati. Thread yang dilihat di dump adalah pemilik jendela yang hang.

Apa yang terjadi pada jendela tingkat atas yang telah dinilai juga didokumentasikan. OS menyembunyikan jendela asli dan menggantinya dengan “jendela hantu” yang punya Z-order, posisi, ukuran, dan tampilan yang sama. Yang dapat dilakukan pengguna hanyalah memindahkan, mengubah ukuran, atau (secara paksa) menutupnya. Aplikasi di dalamnya sebenarnya tidak merespons, jadi operasi lain tidak bekerja.2

Penilaian jendela hang dan pertukaran jendela hantuKetika thread UI diblokir pada pekerjaan berat dan pengambilan pesan berhenti selama 5 detik, OS menilai jendela tidak merespons, menyembunyikan yang asli, menukar masuk jendela hantu dengan tampilan yang sama, dan menawarkan pengguna hanya pindah, minimalkan, dan tutuptidakyaThread UI diblokir pada pekerjaan beratPengambilan pesan berhenti5 detik berlalu?Tukar masuk jendela hantuJudul menampilkan (Tidak Merespons)Putih beku; hanya pindah dan tutup

Gambar 3: Baik teks “Tidak Merespons” maupun layar putih milik jendela hantu yang OS tukar masuk, bukan aplikasi yang hang.

String “(Tidak Merespons)” yang muncul di bilah judul, dan tampilan putih beku di bawah tema Aero, keduanya milik jendela hantu ini. Dua konsekuensi praktis mengikuti.

  • Pada saat “Tidak Merespons” ditampilkan, thread yang memiliki jendela itu belum memproses pesan selama setidaknya 5 detik. Bukan bahwa “tampilan muncul terlalu cepat” — thread UI pasti diblokir.
  • Jendela hantu tidak dibuat sementara debugger terlampir.2 Ketika tampak seolah “ia tidak pernah menjadi Tidak Merespons di bawah debugger, tetapi ia di release”, hang itu sendiri dapat sama dan hanya tampilannya berbeda.

Ada juga API, DisableProcessWindowsGhosting, yang menonaktifkan pertukaran ini untuk seluruh proses.7 Itu dimaksudkan untuk kasus khusus seperti terminal kiosk di mana Anda tidak ingin OS membuat jendela tampak dapat dioperasikan sendiri. Memanggilnya menghentikan tampilan “Tidak Merespons” muncul, tetapi fakta bahwa aplikasi hang tidak berubah. Pahami bahwa ini bukan sesuatu yang aplikasi umum pakai sebagai penanggulangan “Tidak Merespons”.

4. Mengapa aplikasi hang — pola klasik yang memblokir thread UI

Penyebabnya, direbus, adalah satu poin — “thread UI tidak kembali ke message loop” — tetapi bentuk yang Anda temui dalam praktik jatuh ke beberapa klasik.

Klasifikasi penyebab klasik yang memblokir thread UIEmpat keluarga klasik — I/O sinkron dan panggilan jaringan, tunggu kunci, SendMessage lintas thread, dan keterlibatan COM STA — semuanya turun ke satu poin yang sama bahwa thread UI tidak dapat kembali ke message loopPenyebab klasik yang mana?I/O atau kunci?SendMessage atau COM?I/O sinkron dan jaringanTunggu kunciSendMessagelintas threadKeterlibatan COM STAUI tidak dapat kembaliPemeriksaan Tidak Merespons

Gambar 4: Gejala yang terlihat sama, tetapi biang yang memblokir thread jatuh ke empat keluarga, dan penanggulangannya berbeda untuk masing-masing.

I/O sinkron dan panggilan jaringan. Ini yang paling umum. Pola melakukan secara sinkron, di dalam handler klik tombol, baca atau tulis berkas besar, kueri basis data, panggilan Web API, atau akses ke berkas di drive jaringan. Di mesin pengembangan ia selesai dalam sepersekian detik, jadi Anda tidak pernah menyadari; latensi jaringan produksi atau hiccup file-server mengubahnya menjadi tunggu puluhan detik, dan Anda mendapat tiket bahwa “ia hang kadang-kadang”. Drive jaringan punya timeout panjang ketika koneksi putus, dan mereka membuat gejala jauh lebih buruk.

Tunggu kunci. Pola di mana thread UI mencoba mengambil kunci pada data yang dibagi dengan thread worker, dan berakhir menunggu worker yang memegang kunci itu lama. Disiplin kunci dibahas secara rinci di seri multithreading praktis.

SendMessage lintas thread. SendMessage tidak kembali sampai prosedur jendela tujuan selesai memproses.6 Ketika Anda mengirimnya ke jendela di thread lain, pengirim dibuat menunggu sampai thread itu dalam keadaan di mana ia dapat memproses pesan. Jika thread tujuan sendiri menunggu sesuatu, Anda punya deadlock pesan di mana setiap sisi menunggu yang lain.3 Mengirim ke HWND_BROADCAST khususnya akan menyeret Anda masuk segera setelah satu jendela tidak merespons. Ketika Anda tidak mampu menunggu, pertimbangkan SendMessageTimeout atau PostMessage, yang tidak menunggu balasan.8

Deadlock yang disebabkan SendMessage lintas threadJika thread worker mengirim SendMessage ke jendela thread UI sementara thread UI diblokir menunggu hasil worker, setiap sisi menunggu yang lain selesai dan Anda punya deadlockThread workerThread UIThread workerThread UITidak dapat memproses pesan (diblokir)Tidak dapat kembali dari SendMessageMenunggu satu sama lain — deadlockMenunggu worker selesai (diblokir)SendMessage (tidak kembali sampai diproses)

Gambar 5: “Thread UI menunggu worker, dan worker menunggu thread UI lewat SendMessage” adalah deadlock klasik.

Keterlibatan apartment COM. Panggilan ke objek STA dikirim sebagai pesan jendela, jadi ketika thread UI (STA) diblokir, panggilan COM dari thread lain ikut diblokir sebagai kolateral. Struktur itu dijelaskan di artikel COM STA/MTA.

Tumpukan “hanya sekejap”. Bahkan panggilan sinkron 50 md, dipanggil 100 kali dalam loop, adalah 5 detik. Ambang Tidak Merespons adalah 5 detik, tetapi “kelambatan” yang dirasakan mulai sekitar 100 md. Aturan praktis desain adalah “thread UI boleh diblokir hanya selama milidetik”.

5. Desain yang tidak hang — memindahkan pekerjaan berat dari thread UI

Prinsip desain adalah satu hal: pindahkan pekerjaan yang memakan waktu dari thread UI. Di C# (WinForms/WPF), async/await adalah pendekatan tepat yang paling pendek.

private async void RunButton_Click(object sender, EventArgs e)
{
    runButton.Enabled = false;
    try
    {
        // CPU-heavy work, or APIs that are only synchronous, go to a worker via Task.Run
        var result = await Task.Run(() => HeavyCalculation(input));

        // For I/O, use APIs that are natively async (they do not consume a thread either)
        var data = await httpClient.GetStringAsync(url);

        // After await you are back on the UI thread, so you can touch controls directly
        resultLabel.Text = result + data.Length.ToString();
    }
    catch (Exception ex)
    {
        // An exception leaking from an async void handler will take the app down. Catch it here
        MessageBox.Show($"The operation failed: {ex.Message}");
    }
    finally
    {
        runButton.Enabled = true;
    }
}

Ada tiga poin. Pertama, sementara await menunggu, thread UI kembali di message loop, jadi Anda tidak menjadi Tidak Merespons. Kedua, kelanjutan setelah await kembali ke thread UI, jadi Anda dapat menyentuh kontrol secara normal setelahnya (menyentuh kontrol secara langsung dari thread worker dilarang; jika Anda perlu, pakai Control.Invoke / Dispatcher.InvokeAsync).4 Ketiga, nonaktifkan tombol sementara pekerjaan berjalan, dan selain itu bunuh reentrancy lewat desain.

Gambaran yang sama di Win32 native: serahkan pekerjaan ke thread worker, beri tahu thread UI tentang penyelesaian sebagai pesan kustom lewat PostMessage, dan perbarui UI di prosedur jendela. PostMessage hanya menempatkan pesan pada antrian dan segera kembali, jadi sisi worker juga tidak diblokir.6 Tulis menunggu thread worker itu sendiri dengan disiplin yang dibahas di artikel condition variable.

Pembagian peran di aplikasi yang tidak hangThread UI bertanggung jawab hanya untuk menerima input, menampilkan kemajuan, dan menerima pembatalan; thread worker menjalankan pekerjaan berat dan mengembalikan penyelesaian ke thread UI lewat PostMessage atau kelanjutan awaitSerahkan pekerjaanPostMessage / kelanjutan awaitThread UI: input, kemajuan, batalThread worker: pekerjaan beratTidak ada I/O sinkron atau komputasi lama di thread UI

Gambar 6: Pertahankan thread UI sebagai “meja penerimaan”, selalu serahkan pekerjaan berat ke worker, dan ambil hanya notifikasi penyelesaian.

Yang ingin Anda hindari adalah teknik menyisipkan Application.DoEvents() atau loop PeekMessage di antara potongan pekerjaan berat hanya untuk menjaga tampilan tetap hidup. Anda mengelak Tidak Merespons, tetapi event handler sembarang masuk kembali di tengah pekerjaan. Klik kedua pada tombol, menutup form selama pemrosesan, timer menyala — salah satunya dapat merusak data yang masih diproses, dan bug-nya bergantung waktu serta sulit direproduksi. Pertahankan pemompaan message loop secara manual di dalam struktur terbatas seperti dialog kemajuan modal, dan sebagai aturan selesaikan dengan pemisahan.

Linimasa bug reentrancy yang disebabkan DoEventsMemanggil DoEvents di tengah pekerjaan berat membiarkan event handler klik yang diantrekan menginterupsi dan berjalan, menulis ulang data yang masih diproses, lalu melanjutkan pekerjaan asli, menghasilkan korupsi data yang bergantung waktuThread UIThread UIHandler klik-ulang tombol menginterupsiPekerjaan berat mulai (data sedang diproses)DoEvents (proses pesan yang diantrekan)Pekerjaan yang menginterupsi menulis ulang dataPekerjaan asli dilanjutkan (data sudah tidak konsisten)

Gambar 7: DoEvents menghapus “Tidak Merespons” sebagai imbalan mengundang event sembarang ke tengah pekerjaan.

Untuk pekerjaan yang berjalan lama, sertakan tampilan kemajuan dan pembatalan dalam desain juga. Kirim kemajuan ke UI dengan IProgress<T> dan komunikasikan interupsi dengan CancellationToken, dan pengguna dapat melihat bahwa “ia bekerja” dan tidak akan meraih penghentian paksa (yang sering menjadi penyebab korupsi data).

Alur kemajuan dan pembatalan untuk pekerjaan yang berjalan lamaThread worker mengirim kemajuan ke thread UI lewat IProgress; tindakan batal di UI sampai ke worker lewat CancellationToken; worker berhenti pada batas yang nyaman dan membersihkanKemajuan lewat IProgressCancellationTokenWorker: pekerjaan yang berjalan lamaUI: kemajuan dan tombol StopBerhenti pada batas dan bersihkan

Gambar 8: Kemajuan adalah “worker → UI”; pembatalan adalah “UI → worker”. Sertakan saluran dua arah tipis ini dalam desain dari awal.

6. Menyelidiki momen hang

Dalam investigasi “ia hang kadang-kadang”, yang paling berharga adalah keadaan thread pada momen tepat ia hang. Reboot, dan buktinya hilang.

Ambil dump. Di tab Details Task Manager, klik kanan proses target → “Create dump file”. Itu saja memberi Anda full dump dengan stack setiap thread. Hanya dengan memberi tahu staf IT yang menerima tiket “ketika hang, ambil ini sebelum Anda menutupnya” mengubah tingkat keberhasilan investigasi secara besar. Untuk membangun mekanisme pengumpulan, lihat artikel pengumpulan crash dump.

Lihat stack thread UI. Buka dump di WinDbg dan lihat stack thread yang memompa message loop (biasanya thread 0). I/O sinkron muncul sebagai ReadFile atau API jaringan, tunggu kunci sebagai panggilan keluarga WaitFor…, dan SendMessage lintas thread sebagai menunggu di dalam SendMessage — apa adanya. Cara membacanya dijelaskan di artikel pengantar WinDbg.

Lihat secara langsung. Dengan Process Explorer Anda dapat memeriksa daftar thread dan stack di tempat. Ketika ia terus-menerus lamban, ambil jejak WPR dan analisis tunggu thread UI sepanjang waktu (WPR/WPA dalam praktik).

Prosedur dasar menyelidiki Tidak MeresponsAmbil dump pada momen hang, lihat stack thread UI, identifikasi apakah ia berhenti pada I/O sinkron, tunggu kunci, atau SendMessage lintas thread, dan hubungkan itu ke perbaikan desain yang cocokMomen hangAmbil dump (sebelum menutup)Lihat stack thread UII/O sinkron atau tunggu jaringanTunggu kunciSendMessage lintas threadPisahkan situs ke worker

Gambar 9: Bintang investigasi adalah “dump momen hang”; stack thread UI itu sendiri adalah klasifikasi penyebab.

Anda juga dapat menstandarkan cara mengambil potongan pertama dari gejala. Jika ia selalu hang pada operasi tertentu, pertama curigai I/O sinkron di dalam handler itu. Jika ia hang jarang dan tanpa korelasi dengan operasi, curigai urutan kunci atau deadlock SendMessage lintas thread, dan cocokkan target tunggu kedua thread di dump. Jika ia hang hanya di lingkungan tertentu, curigai timeout dari faktor lingkungan seperti drive jaringan, proxy, atau perangkat lunak antivirus.

7. Ringkasan

  • “Tidak Merespons” adalah mekanisme di mana OS menilai bahwa aplikasi belum mengambil pesan selama 5 detik dan menukar masuk jendela hantu. Yang memasang tampilan adalah OS, bukan aplikasi.
  • Penyebab hang adalah satu poin: “thread UI tidak dapat kembali ke message loop”. I/O sinkron, jaringan, tunggu kunci, dan SendMessage lintas thread adalah yang klasik.
  • Penanggulangannya adalah memindahkan pekerjaan berat dari thread UI. Di C#, async/await + Task.Run; di Win32, thread worker + PostMessage. Cegah reentrancy selama eksekusi lewat desain, seperti menonaktifkan tombol.
  • Mengelaknya dengan DoEvents adalah imbalan bug reentrancy. DisableProcessWindowsGhosting hanya menghapus tampilan. Keduanya bukan perbaikan akar.
  • Untuk investigasi, dump “momen hang” adalah yang paling penting. Penyebabnya hampir selalu tertulis di stack thread UI apa adanya.

Dari sudut pandang pengguna “Tidak Merespons” adalah “rusak”, tetapi begitu Anda tahu mekanismenya Anda dapat menerjemahkannya menjadi kalimat tepat “thread UI tidak kembali selama 5 detik”. Bekerja mundur dari satu kalimat itu, kandidat penyebab, perbaikan, dan prosedur investigasi semuanya jatuh secara alami.

Artikel terkait

Area konsultasi terkait

KomuraSoft LLC menangani investigasi akar aplikasi bisnis yang “kadang hang” atau menjadi “Tidak Merespons” (analisis dump dan analisis jejak), refaktor kode UI warisan penuh pekerjaan sinkron menjadi pemisahan async/await dan thread worker, serta tinjauan desain UI yang tidak membeku. Bahkan ketika Anda belum punya prosedur reproduksi, kami dapat membantu mulai dari desain cara mengumpulkan bukti.

Tautan referensi

  1. Microsoft Learn, IsHungAppWindow function (winuser.h). Tentang kriteria penilaian bahwa aplikasi diperlakukan sebagai tidak merespons ketika ia “tidak menunggu input, tidak dalam urutan start-nya, dan belum memanggil PeekMessage selama timeout internal 5 detik”; tentang kriteria 5 detik ini dapat berubah; dan tentang fungsi selalu mengembalikan TRUE untuk jendela hantu.  2

  2. Microsoft Learn, GetMessage function (winuser.h). Tentang sistem memperlakukan jendela tingkat atas sebagai tidak merespons ketika ia berhenti merespons pesan selama beberapa detik dan menggantinya dengan jendela hantu dengan Z-order, posisi, ukuran, dan tampilan yang sama; tentang pengguna hanya dapat memindahkan, mengubah ukuran, atau menutupnya; dan tentang jendela hantu tidak dibuat sementara debugger terlampir.  2 3

  3. Microsoft Learn, About Messages and Message Queues. Tentang aplikasi Windows bersifat event-driven dan prosedur jendela memproses pesan; tentang perbedaan antara pesan yang diantrekan dan pesan yang dikirim langsung; tentang pertukaran jendela yang tidak merespons dengan jendela hantu; dan tentang bagian yang membahas deadlock dari thread yang saling mengirim pesan.  2 3 4

  4. Microsoft Learn, How to make thread-safe calls to controls (Windows Forms). Tentang kontrol WinForms tidak aman disentuh dari thread selain yang membuatnya; tentang memakai Invoke/BeginInvoke untuk pembaruan dari thread lain; dan tentang pola asinkron aman memakai async/await atau BackgroundWorker.  2

  5. Microsoft Learn, Using Messages and Message Queues. Tentang implementasi message-loop tipikal dengan GetMessage, TranslateMessage, dan DispatchMessage, dan tentang cara memeriksa antrian pesan. 

  6. Microsoft Learn, SendMessage function (winuser.h). Tentang SendMessage memanggil prosedur jendela jendela yang ditentukan dan tidak kembali sampai pemrosesan selesai; tentang kirim ke jendela di thread lain yang membuat pengirim menunggu sampai thread itu memproses pesan; dan tentang perbedaan dari PostMessage, yang menempatkan pesan pada antrian tanpa menunggu balasan.  2 3

  7. Microsoft Learn, DisableProcessWindowsGhosting function (winuser.h). Tentang dapat menonaktifkan, untuk proses GUI pemanggil, fitur jendela hantu yang membuat jendela yang tidak merespons dapat diminimalkan, dipindahkan, dan ditutup; dan tentang penonaktifan berlangsung selama masa hidup proses. 

  8. Microsoft Learn, SendMessageTimeout function (winuser.h). Tentang dapat mengirim pesan dengan timeout; dan tentang flag (SMTO_ABORTIFHUNG) yang kembali tanpa menunggu ketika jendela tidak merespons (telah dinilai hang). 

Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.

Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.

Artikel ini berkaitan langsung dengan layanan berikut.

Pertanyaan yang sering diajukan

Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.

Di bawah syarat apa "Tidak Merespons" muncul?
OS menilai jendela hang ketika aplikasi berjendela tidak menunggu input, tidak dalam urutan start-nya, dan belum mengambil pesan (PeekMessage) selama 5 detik. Jendela tingkat atas yang hang disembunyikan dan diganti dengan "jendela hantu" dengan posisi, ukuran, dan tampilan yang sama. Teks bilah judul "(Tidak Merespons)" dan tampilan putih beku milik jendela hantu ini, yang hanya membiarkan Anda memindahkan, meminimalkan, atau menutup. Dengan kata lain, "Tidak Merespons" bukan sesuatu yang aplikasi sendiri tampilkan — itu layar yang OS pasang atas nama aplikasi.
Apakah ada pengaturan agar "Tidak Merespons" tidak muncul sementara pekerjaan berlangsung?
Memanggil DisableProcessWindowsGhosting menonaktifkan pertukaran ke jendela hantu untuk proses itu. Itu hanya membuat hang kurang terlihat bagi pengguna, walaupun — jendela tetap tidak bereaksi terhadap input, dan dari sudut pandang pengguna itu pembekuan total tanpa cara memindahkan atau menutup. Perbaikan sungguhan bukan menekan tampilan, melainkan memindahkan pekerjaan berat ke thread worker agar thread UI tidak pernah diblokir bahkan sepersepuluh detik, apalagi lima. Perhatikan juga bahwa OS tidak membuat jendela hantu sementara debugger terlampir, jadi dapat tampak seolah "Tidak Merespons" tidak pernah terjadi selama debugging.
Apakah boleh menghindari "Tidak Merespons" dengan DoEvents (memompa message loop secara manual)?
Tidak direkomendasikan. Memutar DoEvents atau loop PeekMessage di tengah pekerjaan berat akan mengelak penilaian jendela hang, tetapi setiap event handler kemudian dapat masuk kembali — klik kedua pada tombol, menutup jendela, timer, dan sebagainya. Handler lain yang menulis ulang data yang masih diproses, atau menyentuh form yang seharusnya sudah ditutup dan melempar, menghasilkan bug reentrancy yang bergantung waktu dan sulit direproduksi — lebih buruk dari "Tidak Merespons" itu sendiri. Pendekatan yang tepat adalah memindahkan pekerjaan itu sendiri ke thread worker dengan Task.Run atau serupa, dan membiarkan thread UI bertanggung jawab hanya untuk tampilan kemajuan dan menerima pembatalan.
Bagaimana cara memperbarui UI (kontrol) dari thread worker?
Kontrol WinForms dan elemen WPF boleh disentuh hanya dari thread yang membuatnya (biasanya thread UI). Menyentuhnya secara langsung dari thread worker menyebabkan pengecualian atau perilaku tidak terdefinisi. Di C#, async/await adalah jalur termudah: kelanjutan setelah await kembali ke thread UI pemanggil, jadi Anda dapat memperbarui kontrol secara normal setelah await. Untuk beralih secara eksplisit, pakai Control.Invoke/BeginInvoke di WinForms dan Dispatcher.InvokeAsync di WPF. Di Win32 native, pola yang mapan adalah thread worker PostMessage pesan penyelesaian kustom ke thread UI, dan prosedur jendela memperbarui UI.
Bagaimana cara menyelidiki mengapa aplikasi menampilkan "Tidak Merespons"?
Yang penting adalah menangkap state pada "momen itu sendiri" yang hang. Pertama ambil full dump dari tab Details Task Manager dengan "Create dump file", lalu di WinDbg lihat stack thread UI (thread yang menjalankan message loop). Apakah ia macet di I/O sinkron, tunggu jaringan, tunggu kunci, atau menunggu thread lain lewat SendMessage muncul di stack apa adanya. Untuk melihat proses hidup, daftar thread dan tampilan stack Process Explorer berguna; untuk mengikutinya sepanjang waktu, menangkap jejak WPR efektif. Lihat juga artikel pengantar WinDbg di situs ini, Process Explorer dalam praktik, dan WPR/WPA dalam praktik.

Profil penulis

Halaman perkenalan penulis artikel.

Go Komura

Direktur KomuraSoft LLC

Berspesialisasi dalam pengembangan perangkat lunak Windows, konsultasi teknis, dan investigasi bug, terutama pada proyek dengan sistem yang sudah ada dan bug yang sulit direproduksi.

Kembali ke blog