Apa sebenarnya "Tidak Merespons" — cara Windows memutuskan aplikasi hang, dan cara merancang aplikasi yang tidak hang
· Go Komura · 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
SendMessagelintas thread adalah yang klasik.3 - Prinsip desain adalah “jangan menunggu atau menghitung di thread UI”. Pindahkan pekerjaan berat ke thread worker (
async/await+Task.Rundi C#) dan biarkan thread UI dikhususkan untuk menggambar, kemajuan, dan menerima pembatalan.4 DoEventsdan 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
flowchart TB
accTitle: Struktur dasar message loop
accDescr: OS 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 selesai
os["OS (input, permintaan gambar ulang, timer)"] --> q["Antrian pesan thread"]
q --> gm["Ambil dengan GetMessage"]
gm --> dm["DispatchMessage"]
dm --> wp["Tangani di prosedur jendela"]
wp --> gm
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.
flowchart TB
accTitle: Dua jalur pengiriman pesan
accDescr: PostMessage 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 selesai
pm["PostMessage"] --> q2["Tempatkan pada antrian (segera kembali)"]
q2 --> loop["Loop mengambil dan memproses secara berurutan"]
sm["SendMessage"] --> direct["Panggil prosedur secara langsung"]
direct --> w2["Tidak 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
flowchart TB
accTitle: Penilaian jendela hang dan pertukaran jendela hantu
accDescr: Ketika 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 tutup
busy["Thread UI diblokir pada pekerjaan berat"] --> stop["Pengambilan pesan berhenti"]
stop --> judge{"5 detik berlalu?"}
judge -->|"tidak"| stop
judge -->|"ya"| ghost["Tukar masuk jendela hantu"]
ghost --> u1["Judul menampilkan (Tidak Merespons)"]
ghost --> u2["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.
flowchart TB
accTitle: Klasifikasi penyebab klasik yang memblokir thread UI
accDescr: Empat 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 loop
kind{"Penyebab klasik yang mana?"}
kind --> io{"I/O atau kunci?"}
kind --> other{"SendMessage atau COM?"}
io --> c1["I/O sinkron dan jaringan"]
io --> c2["Tunggu kunci"]
other --> c3["SendMessage"]
c3 -.-> c3n["lintas thread"]
other --> c4["Keterlibatan COM STA"]
c1 --> core["UI tidak dapat kembali"]
c2 --> core
c3 --> core
c4 --> core
core --> ar["Pemeriksaan 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
sequenceDiagram
accTitle: Deadlock yang disebabkan SendMessage lintas thread
accDescr: Jika thread worker mengirim SendMessage ke jendela thread UI sementara thread UI diblokir menunggu hasil worker, setiap sisi menunggu yang lain selesai dan Anda punya deadlock
participant U as Thread UI
participant W as Thread worker
U->>U: Menunggu worker selesai (diblokir)
W->>U: SendMessage (tidak kembali sampai diproses)
Note over U: Tidak dapat memproses pesan (diblokir)
Note over W: Tidak dapat kembali dari SendMessage
Note over U,W: Menunggu satu sama lain — deadlock
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.
flowchart TB
accTitle: Pembagian peran di aplikasi yang tidak hang
accDescr: Thread 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 await
ui["Thread UI: input, kemajuan, batal"] -->|"Serahkan pekerjaan"| w["Thread worker: pekerjaan berat"]
w -->|"PostMessage / kelanjutan await"| ui
ui -.-> ng["Tidak 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.
sequenceDiagram
accTitle: Linimasa bug reentrancy yang disebabkan DoEvents
accDescr: Memanggil 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 waktu
participant U as Thread UI
U->>U: Pekerjaan berat mulai (data sedang diproses)
U->>U: DoEvents (proses pesan yang diantrekan)
Note over U: Handler klik-ulang tombol menginterupsi
U->>U: Pekerjaan yang menginterupsi menulis ulang data
U->>U: Pekerjaan 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).
flowchart TB
accTitle: Alur kemajuan dan pembatalan untuk pekerjaan yang berjalan lama
accDescr: Thread worker mengirim kemajuan ke thread UI lewat IProgress; tindakan batal di UI sampai ke worker lewat CancellationToken; worker berhenti pada batas yang nyaman dan membersihkan
w3["Worker: pekerjaan yang berjalan lama"] -->|"Kemajuan lewat IProgress"| ui2["UI: kemajuan dan tombol Stop"]
ui2 -->|"CancellationToken"| w3
w3 --> stop2["Berhenti 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).
flowchart TB
accTitle: Prosedur dasar menyelidiki Tidak Merespons
accDescr: Ambil 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 cocok
hang["Momen hang"] --> dump["Ambil dump (sebelum menutup)"]
dump --> stack["Lihat stack thread UI"]
stack --> io["I/O sinkron atau tunggu jaringan"]
stack --> lock["Tunggu kunci"]
stack --> sm["SendMessage lintas thread"]
io -.-> fix["Pisahkan situs ke worker"]
lock -.-> fix
sm -.-> fix
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
DoEventsadalah imbalan bug reentrancy.DisableProcessWindowsGhostinghanya 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
- Spurious wakeup — mengapa condition variable bangun “tanpa diberitahu” dan cara menunggu dengan benar di Windows
- Praktik terbaik multithreading praktis: edisi .NET — apa yang diputuskan sebelum Anda menambah lebih banyak thread
- Dasar COM STA/MTA - model threading dan cara menghindari hang
- Membaca crash dump dengan WinDbg + SOS — panduan praktis analisis setelah pengumpulan
- Process Explorer / Handle / VMMap dalam praktik — mengejar hang, kebocoran, dan “berkas sedang dipakai” dari state saat ini
- Shutdown Windows dilihat dari aplikasi Anda — bertahan dari notifikasi keluar, restart, dan kehilangan daya dengan benar
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.
- Investigasi bug dan akar masalah
- Konsultasi teknis dan tinjauan desain
- Pengembangan aplikasi Windows
- Hubungi kami
Tautan referensi
-
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
-
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
-
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
-
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
-
Microsoft Learn, Using Messages and Message Queues. Tentang implementasi message-loop tipikal dengan GetMessage, TranslateMessage, dan DispatchMessage, dan tentang cara memeriksa antrian pesan. ↩
-
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
-
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. ↩
-
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 terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
DllMain dan loader lock — alasan sebenarnya Anda diminta "jangan lakukan apa pun di inisialisasi DLL"
Mengapa Anda tidak boleh memanggil LoadLibrary atau menyinkronkan dengan thread lain dari DllMain. Berdasarkan sumber primer, artikel ini...
Win32 Thread Pool API — konkurensi tanpa membuat thread, lewat CreateThreadpoolWork
Apakah Anda menebar panggilan CreateThread di seluruh kode native? Artikel ini menjelaskan Win32 thread pool API yang didesain ulang di V...
Aplikasi yang rusak saat bangun dari tidur — cara kerja event daya Windows dan cara membangun aplikasi bisnis yang bertahan
Anda membuka laptop dan koneksi aplikasi bisnis sudah mati — penyebabnya adalah desain yang tidak pernah memperhitungkan tidur. Artikel i...
Spurious wakeup — mengapa condition variable bangun "tanpa diberitahu" dan cara menunggu dengan benar di Windows
Tunggu condition variable dapat kembali bahkan ketika tidak ada notifikasi yang datang (spurious wakeup). Artikel ini menjelaskan, dari i...
Cara kerja clipboard dan seret-dan-lepas — menangani transfer data OLE dengan benar di aplikasi bisnis
Tempel tabel Excel dan pemformatan berantakan; tutup aplikasi sumber dan Anda tidak dapat menempel lagi — keduanya datang dari clipboard ...
Topik terkait
Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.
Topik teknis Windows
Portal tentang pengembangan Windows, investigasi bug, dan pemanfaatan aset yang ada.
Thread UI dan timer
Thread UI WPF / WinForms, alur asinkron, Dispatcher, dan desain timer.
Layanan yang terkait dengan topik ini
Artikel ini berkaitan langsung dengan layanan berikut.
Pengembangan aplikasi Windows
Aplikasi bisnis, integrasi perangkat, dan alat komunikasi, dari kebutuhan hingga pengembangan.
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.