"Not Responding" আসলে কী — Windows কীভাবে অ্যাপ হ্যাং ধরে, আর যে ডিজাইন হ্যাং করে না
· Go Komura · Windows, Windows ডেভেলপমেন্ট, সমস্যা সমাধান, মাল্টিথ্রেডিং, WinForms, WPF, Win32 API, UI ডিজাইন
“অপারেশনের মাঝে অ্যাপ সাদা হয়ে (Not Responding) দেখায়।” “মাঝে মাঝে হ্যাং করে এমন টিকিট পাই, কিন্তু ডেভেলপমেন্ট মেশিনে কখনো পুনরুৎপাদন হয় না।” — Windows ব্যবসায়িক অ্যাপে এই “Not Responding” সবচেয়ে সাধারণ অভিযোগের একটি। আর যা আশ্চর্যজনকভাবে কম জানা, তা হলো “Not Responding” ডিসপ্লে তোলা হ্যাং অ্যাপ নিজে নয় — সেটা OS।
Windows কীভাবে জানে অ্যাপ “হ্যাং” করেছে? সেই তুষার-সাদা উইন্ডো কী? Windows-এ ব্যবসায়িক অ্যাপ লেখা ডেভেলপার ও হ্যাং অ্যাপের টিকিট নেওয়া IT কর্মীদের লক্ষ্য করে এই নিবন্ধ মেসেজ লুপের মৌলিক থেকে “Not Responding” বিচার ঘুরে দেখে, আর হ্যাঙের ক্লাসিক কারণ, যে ডিজাইন হ্যাং করে না, ও হ্যাং মুহূর্ত অনুসন্ধানের পদ্ধতি — সব প্রাথমিক উৎসের ভিত্তিতে সাজায়।
১. আগে উপসংহার
- “Not Responding” OS-এর বিচার। উইন্ডো (ও তার মালিক GUI থ্রেড) ইনপুটের অপেক্ষায় না থাকলে, স্টার্টআপ ক্রমে না থাকলে, আর ৫ সেকেন্ড মেসেজ (
PeekMessage) না তুললে OS তাকে সাড়া দিচ্ছে না ধরে। বিচার প্রক্রিয়াপ্রতি নয়।1 - সাদা হওয়া উইন্ডো “ঘোস্ট উইন্ডো”। OS আসল উইন্ডো লুকিয়ে একই অবস্থান, আকার ও চেহারার নকল বসিয়েছে। যা করা যায় শুধু সরানো, মিনিমাইজ বা বন্ধ; ভেতরের বিষয় চলছে না। ডিবাগার লাগানো থাকলে ঘোস্ট উইন্ডো তৈরি হয় না।2
- হ্যাঙের কারণ প্রায় সবসময় এক জিনিসে নেমে আসে। যে UI থ্রেড মেসেজ লুপ পাম্প করবে সে ভারী কাজ বা ওয়েটে ব্লক। সিঙ্ক্রোনাস I/O, নেটওয়ার্ক কল, লক ওয়েট, আর থ্রেড পার করে
SendMessageক্লাসিক।3 - ডিজাইন নীতি “UI থ্রেডে অপেক্ষা বা হিসাব করবেন না”। ভারী কাজ ওয়ার্কার থ্রেডে সরান (C#-এ
async/await+Task.Run) আর UI থ্রেডকে পেইন্টিং, অগ্রগতি ও বাতিল গ্রহণে নিবেদিত রাখুন।4 DoEventsও মেসেজ লুপ নিজে পাম্প করা রিএন্ট্রান্সি বাগের প্রজননক্ষেত্র। “Not Responding” ডিসপ্লে সরে, কিন্তু কাঠামো এখন যেকোনো ইভেন্টকে কাজের মাঝে বাধা দিতে দেয়। এড়ানো নয়, আলাদা করাই সঠিক পথ।- অনুসন্ধান হ্যাং মুহূর্তের স্টেট ধরে শুরু হয়। ডাম্প নিয়ে UI থ্রেডের স্ট্যাক দেখলে প্রায় সবসময় কীসের অপেক্ষা করছে তা চিহ্নিত করা যায়।
২. পূর্বশর্ত: Windows অ্যাপ মেসেজ দিয়ে চলে
“Not Responding” বুঝতে আগে মাথায় নিতে হয় Windows GUI অ্যাপ ইভেন্ট-চালিত। GUI অ্যাপ নিজে গিয়ে ইনপুট তোলে না; OS যে মেসেজ পৌঁছে দেয় (মাউস, কীবোর্ড, রিপেইন্ট অনুরোধ, টাইমার ইত্যাদি) সেগুলো পেয়ে কাজ করে।3
উইন্ডো তৈরি করা প্রতিটি থ্রেডের মেসেজ কিউ থাকে এবং এমন মেসেজ লুপ চালায়।
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg); // the window procedure is called
}
GetMessage কিউ থেকে মেসেজ তোলে, আর DispatchMessage সেই উইন্ডোর উইন্ডো প্রসিডিউর (মেসেজ-হ্যান্ডলিং ফাংশন) ডাকে। বোতাম-ক্লিক হ্যান্ডলিং, রিপেইন্টিং, আর WinForms বা WPF ইভেন্ট হ্যান্ডলার সব, সিদ্ধ করলে, এই লুপের এক ইটারেশনের ভেতরে চলে।5
flowchart TB
accTitle: মেসেজ লুপের মৌলিক কাঠামো
accDescr: OS মাউস, কীবোর্ড ও অন্য ইনপুট থ্রেডের মেসেজ কিউতে রাখে; UI থ্রেডের লুপ GetMessage দিয়ে তোলে, DispatchMessage দিয়ে উইন্ডো প্রসিডিউর ডাকে, আর প্রক্রিয়া শেষে লুপের মাথায় ফেরে
os["OS (ইনপুট, রিপেইন্ট অনুরোধ, টাইমার)"] --> q["থ্রেড মেসেজ কিউ"]
q --> gm["GetMessage দিয়ে তোলা"]
gm --> dm["DispatchMessage"]
dm --> wp["উইন্ডো প্রসিডিউরে সামলানো"]
wp --> gm
চিত্র ১: GUI অ্যাপের হৃদয় মেসেজ লুপ; প্রতিটি ইভেন্ট হ্যান্ডলার এই লুপের এক ইটারেশন হিসেবে চলে।
এই কাঠামোর একটি গুরুত্বপূর্ণ পরিণতি আছে। উইন্ডো প্রসিডিউরের (ইভেন্ট হ্যান্ডলারের) ভেতরে সময়সাপেক্ষ কাজ করলে লুপ তার মধ্যে পরের মেসেজ তুলতে পারে না। ক্লিকেও সাড়া দিতে পারে না, রিপেইন্ট অনুরোধেও না — সেটাই “হ্যাং” আসলে।
মেসেজ দুই পথে পৌঁছায় তাও মাথায় নেওয়া দরকার। PostMessage মেসেজ কিউতে রেখে সঙ্গে সঙ্গে ফেরে, আর লুপ ক্রমে তুলে প্রক্রিয়া করে। SendMessage অন্যদিকে উইন্ডো প্রসিডিউর সরাসরি ডাকে এবং প্রক্রিয়া শেষ না হওয়া পর্যন্ত কলারের কাছে ফেরে না।36 সেই পার্থক্য অধ্যায় ৪-এর ডেডলক আলোচনায় সোজা যায়।
flowchart TB
accTitle: মেসেজ ডেলিভারির দুই পথ
accDescr: PostMessage মেসেজ কিউতে রেখে সঙ্গে সঙ্গে ফেরে; মেসেজ লুপ ক্রমে তুলে প্রক্রিয়া করে। SendMessage উইন্ডো প্রসিডিউর সরাসরি ডাকে এবং প্রক্রিয়া শেষ না হওয়া পর্যন্ত কলারের কাছে ফেরে না
pm["PostMessage"] --> q2["কিউতে রাখা (সঙ্গে সঙ্গে ফেরে)"]
q2 --> loop["লুপ ক্রমে তুলে প্রক্রিয়া করে"]
sm["SendMessage"] --> direct["প্রসিডিউর সরাসরি ডাকা"]
direct --> w2["প্রক্রিয়া শেষ না হওয়া পর্যন্ত ফেরে না"]
চিত্র ২: দুটোই “মেসেজ পাঠায়”, তবু কিউ করা Post ও সম্পূর্ণির অপেক্ষা করা Send-এর প্রকৃতি সম্পূর্ণ আলাদা।
৩. “Not Responding” কীভাবে বিচার হয় — ৫-সেকেন্ড নিয়ম ও ঘোস্ট উইন্ডো
তাহলে OS কীভাবে জানে “এই অ্যাপ হ্যাং করেছে”? মাপকাঠি অফিসিয়ালি নথিভুক্ত। OS উইন্ডোকে সাড়া দিচ্ছে না ধরে যখন সে ইনপুটের অপেক্ষায় নেই, স্টার্টআপ ক্রমে নেই, আর ৫ সেকেন্ড PeekMessage (মেসেজ তোলা) ডাকেনি।1 অর্থাৎ OS “মেসেজ লুপ সত্যি ঘুরছে কি না” নাড়ির মতো দেখে, আর ৫ সেকেন্ড নাড়ি না থাকলে উইন্ডো সাড়া দিচ্ছে না বিচার করে (ডকুমেন্টেশন বলে এই ৫-সেকেন্ড মান ভবিষ্যতে বদলাতে পারে)। বিচারের একক উইন্ডো ও তার মালিক GUI থ্রেড; কয়েকটি UI থ্রেডযুক্ত অ্যাপে একটি থ্রেড হ্যাং মানে অন্য থ্রেডের উইন্ডো মৃত নয়। ডাম্পে যে থ্রেড দেখতে হয় সে হ্যাং উইন্ডোর মালিক।
বিচার হওয়া টপ-লেভেল উইন্ডোর কী হয় তাও নথিভুক্ত। OS আসল উইন্ডো লুকিয়ে একই Z-অর্ডার, অবস্থান, আকার ও চেহারার “ঘোস্ট উইন্ডো” দিয়ে বদলায়। ব্যবহারকারী যা করতে পারে শুধু সরানো, আকার বদলানো, অথবা (জোর করে) বন্ধ। ভেতরের অ্যাপ সত্যি সাড়া দিচ্ছে না, তাই অন্য অপারেশন কাজ করে না।2
flowchart TB
accTitle: হ্যাং-উইন্ডো বিচার ও ঘোস্ট-উইন্ডো বদল
accDescr: UI থ্রেড ভারী কাজে ব্লক হয়ে মেসেজ তোলা ৫ সেকেন্ড থামলে OS উইন্ডোকে সাড়া দিচ্ছে না বিচার করে, আসল লুকিয়ে একই চেহারার ঘোস্ট উইন্ডো বসায়, আর ব্যবহারকারীকে শুধু সরানো, মিনিমাইজ ও বন্ধ দেয়
busy["UI থ্রেড ভারী কাজে ব্লক"] --> stop["মেসেজ তোলা থামে"]
stop --> judge{"৫ সেকেন্ড পার?"}
judge -->|"না"| stop
judge -->|"হ্যাঁ"| ghost["ঘোস্ট উইন্ডো বসানো"]
ghost --> u1["টাইটেলে (Not Responding)"]
ghost --> u2["তুষার সাদা; শুধু সরানো ও বন্ধ"]
চিত্র ৩: “Not Responding” টেক্সট ও সাদা স্ক্রিন দুটোই OS যে ঘোস্ট উইন্ডো বসিয়েছে তার, হ্যাং অ্যাপের নয়।
টাইটেল বারে যে “(Not Responding)” স্ট্রিং দেখা যায়, আর Aero থিমের তুষার-সাদা চেহারা, দুটোই এই ঘোস্ট উইন্ডোর। দুটি ব্যবহারিক পরিণতি আসে।
- “Not Responding” দেখানোর সময় সেই উইন্ডোর মালিক থ্রেড অন্তত ৫ সেকেন্ড মেসেজ প্রক্রিয়া করছে না। “ডিসপ্লে খুব তাড়াতাড়ি এসেছে” নয় — UI থ্রেড নিশ্চিত ব্লক।
- ডিবাগার লাগানো থাকলে ঘোস্ট উইন্ডো তৈরি হয় না।2 “ডিবাগারে কখনো Not Responding হয় না, কিন্তু রিলিজে হয়” মনে হলে হ্যাং নিজে একই হতে পারে আর শুধু ডিসপ্লে আলাদা।
একটি APIও আছে, DisableProcessWindowsGhosting, যা পুরো প্রক্রিয়ার জন্য এই বদল নিষ্ক্রিয় করে।7 কিয়স্ক টার্মিনালের মতো বিশেষ ক্ষেত্রে যেখানে OS নিজে উইন্ডোকে চালানো যায় বলে দেখাতে চান না। ডাকলে “Not Responding” ডিসপ্লে আসে না, কিন্তু অ্যাপ হ্যাং এই সত্য বদলায় না। বুঝুন, সাধারণ অ্যাপ এটাকে “Not Responding” প্রতিরোধ হিসেবে ব্যবহার করে না।
৪. অ্যাপ কেন হ্যাং করে — UI থ্রেড ব্লক করার ক্লাসিক আকৃতি
কারণ, সিদ্ধ করলে, একটি বিন্দু — “UI থ্রেড মেসেজ লুপে ফেরে না” — কিন্তু বাস্তবে যে আকৃতি দেখা যায় সেগুলো কয়েকটি ক্লাসিকে পড়ে।
flowchart TB
accTitle: UI থ্রেড ব্লক করা ক্লাসিক কারণের শ্রেণিবিভাগ
accDescr: চার ক্লাসিক পরিবার — সিঙ্ক্রোনাস I/O ও নেটওয়ার্ক কল, লক ওয়েট, থ্রেড পার করে SendMessage, আর COM STA জড়িত হওয়া — সব একই এক বিন্দুতে নেমে আসে যে UI থ্রেড মেসেজ লুপে ফিরতে পারে না
kind{"কোন ক্লাসিক কারণ?"}
kind --> io{"I/O নাকি লক?"}
kind --> other{"SendMessage নাকি COM?"}
io --> c1["সিঙ্ক I/O ও নেটওয়ার্ক"]
io --> c2["লক ওয়েট"]
other --> c3["SendMessage"]
c3 -.-> c3n["থ্রেড পার করে"]
other --> c4["COM STA জড়িত"]
c1 --> core["UI ফিরতে পারে না"]
c2 --> core
c3 --> core
c4 --> core
core --> ar["Not Responding যাচাই"]
চিত্র ৪: দৃশ্যমান উপসর্গ একই, কিন্তু থ্রেড ব্লক করা অপরাধী চার পরিবারে পড়ে, আর প্রতিটির প্রতিরোধ আলাদা।
সিঙ্ক্রোনাস I/O ও নেটওয়ার্ক কল। এটি সবচেয়ে সাধারণ। বোতাম-ক্লিক হ্যান্ডলারের ভেতরে বড় ফাইল পড়া বা লেখা, ডেটাবেস কোয়েরি, Web API কল, অথবা নেটওয়ার্ক ড্রাইভের ফাইল অ্যাক্সেস সিঙ্ক্রোনাসভাবে করার আকৃতি। ডেভেলপমেন্ট মেশিনে ভগ্নাংশ সেকেন্ডে শেষ হয়, তাই টের পান না; প্রোডাকশন নেটওয়ার্ক লেটেন্সি বা ফাইল-সার্ভার হিচকিতে কয়েক দশ সেকেন্ডের ওয়েট হয়ে যায়, আর “মাঝে মাঝে হ্যাং করে” টিকিট আসে। নেটওয়ার্ক ড্রাইভ সংযোগ না থাকলে দীর্ঘ টাইমআউট রাখে, আর উপসর্গ নাটকীয়ভাবে খারাপ করে।
লক ওয়েট। যে আকৃতিতে UI থ্রেড ওয়ার্কার থ্রেডের সাথে শেয়ার করা ডেটার লক নিতে যায়, আর সেই লক দীর্ঘ সময় ধরে রাখা ওয়ার্কারের অপেক্ষা করে। লক শৃঙ্খলা ব্যবহারিক মাল্টিথ্রেডিং সিরিজে বিস্তারিত।
থ্রেড পার করে SendMessage। SendMessage গন্তব্য উইন্ডোর প্রসিডিউর প্রক্রিয়া শেষ না করা পর্যন্ত ফেরে না।6 অন্য থ্রেডের উইন্ডোতে পাঠালে প্রেরককে অপেক্ষা করানো হয় সেই থ্রেড মেসেজ প্রক্রিয়া করতে পারা অবস্থায় না আসা পর্যন্ত। গন্তব্য থ্রেড নিজেই কিছুর অপেক্ষা করলে মেসেজ ডেডলক হয় যেখানে দুই পাশ পরস্পরের অপেক্ষা করে।3 বিশেষ করে HWND_BROADCAST-এ পাঠালে একটি উইন্ডো সাড়া না দিলেই টেনে নামান। অপেক্ষা চলবে না হলে SendMessageTimeout অথবা উত্তরের অপেক্ষা করে না এমন PostMessage ভাবুন।8
sequenceDiagram
accTitle: থ্রেড পার করে SendMessage-এর ডেডলক
accDescr: UI থ্রেড ওয়ার্কারের ফলের অপেক্ষায় ব্লক থাকাকালীন ওয়ার্কার থ্রেড UI-থ্রেড উইন্ডোতে SendMessage পাঠালে দুই পাশ পরস্পরের শেষের অপেক্ষা করে ডেডলক হয়
participant U as UI থ্রেড
participant W as ওয়ার্কার থ্রেড
U->>U: ওয়ার্কার শেষের অপেক্ষা (ব্লক)
W->>U: SendMessage (প্রক্রিয়া না হওয়া পর্যন্ত ফেরে না)
Note over U: মেসেজ প্রক্রিয়া করতে পারে না (ব্লক)
Note over W: SendMessage থেকে ফিরতে পারে না
Note over U,W: পরস্পর অপেক্ষা — ডেডলক
চিত্র ৫: “UI থ্রেড ওয়ার্কারের অপেক্ষা করে, আর ওয়ার্কার SendMessage দিয়ে UI থ্রেডের অপেক্ষা করে” ক্লাসিক ডেডলক।
COM অ্যাপার্টমেন্ট জড়িত হওয়া। STA অবজেক্টে কল উইন্ডো মেসেজ হিসেবে পৌঁছায়, তাই UI থ্রেড (STA) ব্লক থাকলে অন্য থ্রেডের COM কলও সহগামীভাবে ব্লক হয়। সেই কাঠামো COM STA/MTA নিবন্ধে ব্যাখ্যা করা।
“শুধু এক মুহূর্ত”-এর স্তূপ। ৫০ ms সিঙ্ক্রোনাস কলও লুপে ১০০ বার ডাকলে ৫ সেকেন্ড। Not Responding সীমা ৫ সেকেন্ড, কিন্তু অনুভূত “ধীরতা” প্রায় ১০০ ms থেকে শুরু। ডিজাইন অঙ্গুষ্ঠনিয়ম “UI থ্রেড শুধু মিলিসেকেন্ড ব্লক হতে পারে”।
৫. যে ডিজাইন হ্যাং করে না — ভারী কাজ UI থ্রেড থেকে সরানো
ডিজাইন নীতি একটাই: সময়সাপেক্ষ কাজ UI থ্রেড থেকে সরান। C#-এ (WinForms/WPF) async/await সবচেয়ে ছোট সঠিক পথ।
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;
}
}
তিনটি বিষয়। প্রথম, await অপেক্ষা করাকালীন UI থ্রেড মেসেজ লুপে ফিরে, তাই Not Responding হয় না। দ্বিতীয়, await-এর পরের কন্টিনিউয়েশন UI থ্রেডে ফেরে, তাই পরে কন্ট্রোল স্বাভাবিকভাবে ছোঁয়া যায় (ওয়ার্কার থ্রেড থেকে কন্ট্রোল সরাসরি ছোঁয়া নিষিদ্ধ; দরকার হলে Control.Invoke / Dispatcher.InvokeAsync)।4 তৃতীয়, কাজ চলাকালীন বোতাম নিষ্ক্রিয় করুন, আর অন্যভাবে ডিজাইনে রিএন্ট্রান্সি মারুন।
নেটিভ Win32-এ ছবি একই: কাজ ওয়ার্কার থ্রেডে দিন, সম্পূর্ণি PostMessage দিয়ে কাস্টম মেসেজ হিসেবে UI থ্রেডকে জানান, আর উইন্ডো প্রসিডিউরে UI আপডেট করুন। PostMessage শুধু মেসেজ কিউতে রেখে সঙ্গে সঙ্গে ফেরে, তাই ওয়ার্কার পাশও ব্লক হয় না।6 ওয়ার্কার থ্রেডের অপেক্ষা নিজে কন্ডিশন-ভেরিয়েবল নিবন্ধে বর্ণিত শৃঙ্খলায় লিখুন।
flowchart TB
accTitle: যে অ্যাপ হ্যাং করে না তার ভূমিকা ভাগ
accDescr: UI থ্রেড শুধু ইনপুট গ্রহণ, অগ্রগতি দেখানো ও বাতিল গ্রহণের দায়িত্বে; ওয়ার্কার থ্রেড ভারী কাজ চালায় আর PostMessage বা await কন্টিনিউয়েশন দিয়ে সম্পূর্ণি UI থ্রেডে ফেরায়
ui["UI থ্রেড: ইনপুট, অগ্রগতি, বাতিল"] -->|"কাজ হস্তান্তর"| w["ওয়ার্কার থ্রেড: ভারী কাজ"]
w -->|"PostMessage / await কন্টিনিউয়েশন"| ui
ui -.-> ng["UI থ্রেডে সিঙ্ক্রোনাস I/O বা দীর্ঘ হিসাব নেই"]
চিত্র ৬: UI থ্রেডকে “রিসেপশন ডেস্ক” রাখুন, ভারী কাজ সবসময় ওয়ার্কারকে দিন, আর শুধু সম্পূর্ণি নোটিফিকেশন নিন।
যা এড়াতে চান তা ভারী কাজের টুকরোর মাঝে শুধু ডিসপ্লে বাঁচাতে Application.DoEvents() বা PeekMessage লুপ ঢোকানোর কৌশল। Not Responding এড়ান, কিন্তু যেকোনো ইভেন্ট হ্যান্ডলার কাজের মাঝে রি-এন্টার করে। বোতামের দ্বিতীয় ক্লিক, প্রক্রিয়ার সময় ফর্ম বন্ধ, টাইমার ফায়ার — যেকোনোটা এখনও প্রক্রিয়াধীন ডেটা দূষিত করতে পারে, আর বাগ সময়নির্ভর ও পুনরুৎপাদন-কঠিন। মেসেজ লুপ নিজে পাম্প করা মোডাল অগ্রগতি ডায়ালগের মতো সীমিত কাঠামোর ভেতরে রাখুন, আর নিয়ম হিসেবে আলাদা করে সমাধান করুন।
sequenceDiagram
accTitle: DoEvents-এর রিএন্ট্রান্সি বাগের টাইমলাইন
accDescr: ভারী কাজের মাঝে DoEvents ডাকলে কিউ করা ক্লিকের ইভেন্ট হ্যান্ডলার বাধা দিয়ে চলে, এখনও প্রক্রিয়াধীন ডেটা আবার লেখে, তারপর আসল কাজ আবার শুরু হয় — সময়নির্ভর ডেটা দূষণ
participant U as UI থ্রেড
U->>U: ভারী কাজ শুরু (ডেটা প্রক্রিয়াধীন)
U->>U: DoEvents (কিউ করা মেসেজ প্রক্রিয়া)
Note over U: বোতাম-আবার-ক্লিক হ্যান্ডলার বাধা দেয়
U->>U: বাধা দেওয়া কাজ ডেটা আবার লেখে
U->>U: আসল কাজ আবার চলে (ডেটা ইতিমধ্যে অসঙ্গত)
চিত্র ৭: DoEvents “Not Responding” মুছে দেয় বিনিময়ে যেকোনো ইভেন্টকে কাজের মাঝে ডাকে।
দীর্ঘ-চলমান কাজে অগ্রগতি ডিসপ্লে ও বাতিলও ডিজাইনে রাখুন। IProgress<T> দিয়ে অগ্রগতি UI-তে পাঠান আর CancellationToken দিয়ে বাধা জানান, আর ব্যবহারকারী “কাজ করছে” দেখতে পায় এবং জোর করে শেষ করতে হাত বাড়াবে না (যা প্রায়ই ডেটা দূষণের কারণ)।
flowchart TB
accTitle: দীর্ঘ-চলমান কাজের অগ্রগতি ও বাতিল প্রবাহ
accDescr: ওয়ার্কার থ্রেড IProgress দিয়ে অগ্রগতি UI থ্রেডে পাঠায়; UI-এর বাতিল কাজ CancellationToken দিয়ে ওয়ার্কারে পৌঁছায়; ওয়ার্কার সুবিধাজনক সীমানায় থেমে পরিষ্কার করে
w3["ওয়ার্কার: দীর্ঘ-চলমান কাজ"] -->|"IProgress দিয়ে অগ্রগতি"| ui2["UI: অগ্রগতি ও স্টপ বোতাম"]
ui2 -->|"CancellationToken"| w3
w3 --> stop2["সীমানায় থেমে পরিষ্কার"]
চিত্র ৮: অগ্রগতি “ওয়ার্কার → UI”; বাতিল “UI → ওয়ার্কার”। এই পাতলা দ্বিমুখী চ্যানেল শুরু থেকে ডিজাইনে রাখুন।
৬. হ্যাং মুহূর্ত অনুসন্ধান
“মাঝে মাঝে হ্যাং করে” অনুসন্ধানে সবচেয়ে মূল্যবান জিনিস ঠিক যে মুহূর্তে হ্যাং সেই থ্রেড স্টেট। রিবুট করলে প্রমাণ চলে যায়।
ডাম্প নিন। Task Manager-এর Details ট্যাবে টার্গেট প্রক্রিয়ায় রাইট-ক্লিক → “Create dump file”। শুধু তাতেই প্রতিটি থ্রেডের স্ট্যাকসহ ফুল ডাম্প পাওয়া যায়। টিকিট নেওয়া IT কর্মীকে শুধু বলা “হ্যাং করলে বন্ধ করার আগে এটা নিন” অনুসন্ধানের সাফল্যের হার অনেক বদলায়। সংগ্রহ যন্ত্র গড়তে ক্র্যাশ-ডাম্প সংগ্রহ নিবন্ধ দেখুন।
UI থ্রেডের স্ট্যাক দেখুন। WinDbg-এ ডাম্প খুলে মেসেজ লুপ পাম্প করা থ্রেডের স্ট্যাক দেখুন (সাধারণত থ্রেড ০)। সিঙ্ক্রোনাস I/O ReadFile বা নেটওয়ার্ক API হিসেবে দেখায়, লক ওয়েট WaitFor… পরিবারের কল হিসেবে, আর থ্রেড পার করে SendMessage SendMessage-এর ভেতরে অপেক্ষা হিসেবে — যেমন আছে তেমন। কীভাবে পড়তে হয় পরিচায়ক WinDbg নিবন্ধে।
লাইভ দেখুন। Process Explorer দিয়ে থ্রেড তালিকা ও স্ট্যাক তখনই পরিদর্শন করা যায়। স্থিরভাবে ধীর থাকলে WPR ট্রেস নিয়ে সময় ধরে UI থ্রেডের ওয়েট বিশ্লেষণ করুন (ব্যবহারিক WPR/WPA)।
flowchart TB
accTitle: Not Responding অনুসন্ধানের মৌলিক পদ্ধতি
accDescr: হ্যাং মুহূর্তে ডাম্প নিন, UI থ্রেডের স্ট্যাক দেখুন, সিঙ্ক্রোনাস I/O, লক ওয়েট নাকি থ্রেড পার করে SendMessage-এ থেমেছে চিহ্নিত করুন, আর মিল থাকা ডিজাইন সংশোধনে যোগ করুন
hang["হ্যাং মুহূর্ত"] --> dump["ডাম্প নিন (বন্ধ করার আগে)"]
dump --> stack["UI থ্রেডের স্ট্যাক দেখুন"]
stack --> io["সিঙ্ক্রোনাস I/O বা নেটওয়ার্ক ওয়েট"]
stack --> lock["লক ওয়েট"]
stack --> sm["থ্রেড পার করে SendMessage"]
io -.-> fix["সাইট ওয়ার্কারে আলাদা করুন"]
lock -.-> fix
sm -.-> fix
চিত্র ৯: অনুসন্ধানের নায়ক “হ্যাং মুহূর্তের ডাম্প”; UI থ্রেডের স্ট্যাকই কারণের শ্রেণিবিভাগ।
উপসর্গ থেকে প্রথম কাট কীভাবে নেবেন তাও মানক করা যায়। সবসময় নির্দিষ্ট অপারেশনে হ্যাং করলে সেই হ্যান্ডলারের ভেতরের সিঙ্ক্রোনাস I/O আগে সন্দেহ করুন। বিরলে ও অপারেশনের সাথে সম্পর্কহীন হ্যাং করলে লক অর্ডার বা থ্রেড পার করে SendMessage ডেডলক সন্দেহ করুন, আর ডাম্পে দুই থ্রেডের ওয়েট টার্গেট মিলান। শুধু নির্দিষ্ট পরিবেশে হ্যাং করলে নেটওয়ার্ক ড্রাইভ, প্রক্সি বা অ্যান্টিভাইরাস সফটওয়্যারের মতো পরিবেশগত কারণের টাইমআউট সন্দেহ করুন।
৭. সারসংক্ষেপ
- “Not Responding” এমন যন্ত্র যেখানে OS বিচার করে অ্যাপ ৫ সেকেন্ড মেসেজ তোলেনি আর ঘোস্ট উইন্ডো বসায়। ডিসপ্লে তোলা OS, অ্যাপ নয়।
- হ্যাঙের কারণ একটি বিন্দু: “UI থ্রেড মেসেজ লুপে ফিরতে পারে না”। সিঙ্ক্রোনাস I/O, নেটওয়ার্ক, লক ওয়েট ও থ্রেড পার করে SendMessage ক্লাসিক।
- প্রতিরোধ ভারী কাজ UI থ্রেড থেকে সরানো। C#-এ
async/await+Task.Run; Win32-এ ওয়ার্কার থ্রেড +PostMessage। চালনার সময় বোতাম নিষ্ক্রিয় করার মতো ডিজাইনে রিএন্ট্রান্সি আটকান। DoEventsদিয়ে এড়ানো রিএন্ট্রান্সি বাগের বিনিময়।DisableProcessWindowsGhostingশুধু ডিসপ্লে সরায়। কোনোটাই মূল-কারণ সমাধান নয়।- অনুসন্ধানে “হ্যাং মুহূর্ত”-এর ডাম্প সবচেয়ে গুরুত্বপূর্ণ। কারণ প্রায় সবসময় UI থ্রেডের স্ট্যাকে যেমন আছে তেমন লেখা।
ব্যবহারকারীর দৃষ্টিতে “Not Responding” মানে “ভাঙছে”, কিন্তু যন্ত্র জানলে তাকে সঠিক বাক্যে অনুবাদ করা যায়: “UI থ্রেড ৫ সেকেন্ড ফেরেনি”। সেই এক বাক্য থেকে পেছনে হাঁটলে প্রার্থী কারণ, সমাধান ও অনুসন্ধান পদ্ধতি স্বাভাবিকভাবেই বেরোয়।
সম্পর্কিত নিবন্ধ
- Spurious Wakeup — কন্ডিশন ভেরিয়েবল কেন “নোটিফাই না হয়েও” জাগে এবং Windows-এ সঠিকভাবে কীভাবে অপেক্ষা করবেন
- ব্যবহারিক মাল্টিথ্রেডিং সেরা অনুশীলন: .NET সংস্করণ — আরও থ্রেড যোগ করার আগে কী ঠিক করবেন
- COM STA/MTA মৌলিক কথা - থ্রেডিং মডেল ও হ্যাং কীভাবে এড়াবেন
- WinDbg + SOS দিয়ে ক্র্যাশ ডাম্প পড়া — সংগ্রহের পর বিশ্লেষণের ব্যবহারিক গাইড
- ব্যবহারিক Process Explorer / Handle / VMMap — হ্যাং, লিক ও “ফাইল ব্যবহৃত” এখনকার স্টেট থেকে তাড়া
- আপনার অ্যাপের চোখে Windows শাটডাউন — এক্সিট নোটিফিকেশন, রিস্টার্ট ও পাওয়ার হারানো সঠিকভাবে সহ্য করা
সম্পর্কিত পরামর্শ ক্ষেত্র
KomuraSoft LLC “মাঝে মাঝে হ্যাং করে” বা “Not Responding” হয় এমন ব্যবসায়িক অ্যাপের মূল কারণ অনুসন্ধান (ডাম্প বিশ্লেষণ ও ট্রেস বিশ্লেষণ), সিঙ্ক্রোনাস কাজে ভরা লেগাসি UI কোড async/await ও ওয়ার্কার-থ্রেড আলাদাকরণে রিফ্যাক্টর, আর যে UI ডিজাইন জমে না তার রিভিউ সামলায়। পুনরুৎপাদন পদ্ধতি এখনও না থাকলেও প্রমাণ কীভাবে সংগ্রহ করবেন তার ডিজাইন থেকে সাহায্য করা যায়।
- বাগ অনুসন্ধান ও মূল কারণ বিশ্লেষণ
- টেকনিক্যাল কনসালটিং ও ডিজাইন রিভিউ
- Windows অ্যাপ্লিকেশন ডেভেলপমেন্ট
- যোগাযোগ করুন
তথ্যসূত্র
-
Microsoft Learn, IsHungAppWindow function (winuser.h)। অ্যাপকে সাড়া দিচ্ছে না ধরার বিচার মাপকাঠি যে সে “ইনপুটের অপেক্ষায় নেই, স্টার্টআপ ক্রমে নেই, আর অভ্যন্তরীণ ৫-সেকেন্ড টাইমআউটে PeekMessage ডাকেনি”; এই ৫-সেকেন্ড মাপকাঠি বদলাতে পারে; এবং ঘোস্ট উইন্ডোর জন্য ফাংশন সবসময় TRUE ফেরানো সম্পর্কে। ↩ ↩2
-
Microsoft Learn, GetMessage function (winuser.h)। টপ-লেভেল উইন্ডো কয়েক সেকেন্ড মেসেজে সাড়া না দিলে সিস্টেম তাকে সাড়া দিচ্ছে না ধরে একই Z-অর্ডার, অবস্থান, আকার ও চেহারার ঘোস্ট উইন্ডো দিয়ে বদলানো; ব্যবহারকারী শুধু সরাতে, আকার বদলাতে বা বন্ধ করতে পারে; এবং ডিবাগার লাগানো থাকলে ঘোস্ট উইন্ডো তৈরি না হওয়া সম্পর্কে। ↩ ↩2 ↩3
-
Microsoft Learn, About Messages and Message Queues। Windows অ্যাপ ইভেন্ট-চালিত ও উইন্ডো প্রসিডিউর মেসেজ প্রক্রিয়া করা; কিউ করা মেসেজ ও সরাসরি পাঠানো মেসেজের পার্থক্য; সাড়া-না-দেওয়া উইন্ডোকে ঘোস্ট উইন্ডো দিয়ে বদল; এবং থ্রেড পরস্পরকে মেসেজ পাঠালে ডেডলক নিয়ে সেকশন সম্পর্কে। ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, How to make thread-safe calls to controls (Windows Forms)। WinForms কন্ট্রোল তৈরি করা থ্রেড ছাড়া অন্য কোনো থ্রেড থেকে ছোঁয়া নিরাপদ নয়; অন্য থ্রেড থেকে আপডেটে Invoke/BeginInvoke ব্যবহার; এবং async/await বা BackgroundWorker দিয়ে নিরাপদ অ্যাসিঙ্ক্রোনাস আকৃতি সম্পর্কে। ↩ ↩2
-
Microsoft Learn, Using Messages and Message Queues। GetMessage, TranslateMessage ও DispatchMessage দিয়ে ক্লাসিক মেসেজ-লুপ বাস্তবায়ন, আর মেসেজ কিউ কীভাবে পরিদর্শন করবেন সম্পর্কে। ↩
-
Microsoft Learn, SendMessage function (winuser.h)। SendMessage নির্দিষ্ট উইন্ডোর উইন্ডো প্রসিডিউর ডাকে এবং প্রক্রিয়া শেষ না হওয়া পর্যন্ত ফেরে না; অন্য থ্রেডের উইন্ডোতে পাঠালে প্রেরক সেই থ্রেড মেসেজ প্রক্রিয়া না করা পর্যন্ত অপেক্ষা করে; এবং উত্তরের অপেক্ষা না করে কিউতে রাখা PostMessage-এর পার্থক্য সম্পর্কে। ↩ ↩2 ↩3
-
Microsoft Learn, DisableProcessWindowsGhosting function (winuser.h)। কলিং GUI প্রক্রিয়ার জন্য ঘোস্ট-উইন্ডো ফিচার নিষ্ক্রিয় করা যায় যা সাড়া-না-দেওয়া উইন্ডোকে মিনিমাইজ, সরানো ও বন্ধযোগ্য করে; এবং নিষ্ক্রিয়তা প্রক্রিয়ার জীবনকাল ধরে থাকা সম্পর্কে। ↩
-
Microsoft Learn, SendMessageTimeout function (winuser.h)। টাইমআউটসহ মেসেজ পাঠানো যায়; এবং উইন্ডো সাড়া না দিলে (হ্যাং বিচার হলে) অপেক্ষা না করে ফেরার ফ্ল্যাগ (SMTO_ABORTIFHUNG) সম্পর্কে। ↩
সম্পর্কিত নিবন্ধ
কাছাকাছি বিষয়ে গভীরে যেতে একই ট্যাগযুক্ত সাম্প্রতিক নিবন্ধ।
DllMain ও লোডার লক — DLL ইনিশিয়ালাইজেশনে "কিছুই করবেন না" বলা হয় তার আসল কারণ
DllMain থেকে LoadLibrary ডাকা বা অন্য থ্রেডের সাথে সিঙ্ক্রোনাইজ করা যায় না কেন। প্রাথমিক উৎস থেকে এই নিবন্ধ ব্যাখ্যা করে লোডার লক প্রতিট...
Win32 থ্রেড পুল API — CreateThreadpoolWork দিয়ে থ্রেড তৈরি না করে কনকারেন্সি
নেটিভ কোডে চারদিকে CreateThread ডাকছেন? এই নিবন্ধ Vista-তে নতুন করে সাজানো Win32 থ্রেড পুল API ব্যাখ্যা করে — work, timer, wait ও io চারট...
ঘুম থেকে জাগলে ভাঙে যে অ্যাপ — Windows পাওয়ার ইভেন্টের কাজ ও যে ব্যবসায়িক অ্যাপ তা সহ্য করে
ল্যাপটপ খুললেন আর ব্যবসায়িক অ্যাপের সংযোগ মৃত — কারণ ঘুম ধরে না নেওয়া ডিজাইন। এই নিবন্ধ WM_POWERBROADCAST নোটিফিকেশন প্রবাহ, Modern Sta...
Spurious Wakeup — কন্ডিশন ভেরিয়েবল কেন "নোটিফাই না হয়েও" জাগে এবং Windows-এ সঠিকভাবে কীভাবে অপেক্ষা করবেন
কন্ডিশন ভেরিয়েবলের ওয়েট নোটিফিকেশন না এলেও ফিরতে পারে (spurious wakeup)। এই নিবন্ধ Windows বাস্তবায়ন থেকে ব্যাখ্যা করে স্পেসিফিকেশন কে...
ক্লিপবোর্ড ও ড্র্যাগ অ্যান্ড ড্রপ কীভাবে কাজ করে — ব্যবসায়িক অ্যাপে OLE ডেটা ট্রান্সফার সঠিকভাবে সামলানো
Excel টেবিল পেস্ট করলে ফরম্যাটিং ভেঙে যায়; সোর্স অ্যাপ বন্ধ করলে আর পেস্ট করা যায় না — দুটোই আসে ক্লিপবোর্ড একই বিষয়বস্তু একসঙ্গে একাধ...
সম্পর্কিত বিষয়
এই পৃষ্ঠাগুলো বিষয়টিকে সেবা ও সিদ্ধান্তের বৃহত্তর প্রেক্ষাপটে স্থাপন করে।
Windows-এর প্রযুক্তিগত বিষয়
Windows ডেভেলপমেন্ট, বাগ তদন্ত ও বিদ্যমান সম্পদ ব্যবহারের প্রবেশদ্বার।
UI থ্রেড ও টাইমার
WPF / WinForms-এর UI থ্রেড, অ্যাসিনক্রোনাস প্রবাহ, Dispatcher ও টাইমার ডিজাইন।
এই বিষয়ের সাথে সম্পর্কিত সেবা
নিবন্ধটি নিচের সেবাগুলোর সাথে সরাসরি সম্পর্কিত।
Windows অ্যাপ ডেভেলপমেন্ট
ব্যবসায়িক অ্যাপ, ডিভাইস ইন্টিগ্রেশন ও যোগাযোগ টুল, চাহিদা থেকে ডেভেলপমেন্ট পর্যন্ত।
প্রায়শ জিজ্ঞাসিত প্রশ্ন
এই নিবন্ধের বিষয়ে পরামর্শে প্রায়ই আসা প্রশ্ন।
- কোন শর্তে "Not Responding" দেখা যায়?
- OS উইন্ডোকে হ্যাং ধরে যখন উইন্ডোযুক্ত অ্যাপ ইনপুটের অপেক্ষায় নেই, স্টার্টআপ ক্রমে নেই, আর ৫ সেকেন্ড মেসেজ (PeekMessage) তোলেনি। হ্যাং টপ-লেভেল উইন্ডো লুকিয়ে একই অবস্থান, আকার ও চেহারার "ঘোস্ট উইন্ডো" দিয়ে বদলানো হয়। "(Not Responding)" টাইটেল-বার টেক্সট ও তুষার-সাদা চেহারা এই ঘোস্ট উইন্ডোর, যা শুধু সরানো, মিনিমাইজ বা বন্ধ করতে দেয়। অর্থাৎ "Not Responding" অ্যাপ নিজে দেখায় না — OS অ্যাপের পক্ষে যে স্ক্রিন তোলে।
- কাজ চলাকালীন "Not Responding" না দেখাতে কোনো সেটিং আছে?
- DisableProcessWindowsGhosting ডাকলে সেই প্রক্রিয়ার জন্য ঘোস্ট উইন্ডোতে বদল নিষ্ক্রিয় হয়। তবে তা শুধু ব্যবহারকারীর চোখে হ্যাং কম দেখায় — উইন্ডো এখনও ইনপুটে সাড়া দেয় না, আর ব্যবহারকারীর দৃষ্টিতে সরানো বা বন্ধ করার উপায়হীন সম্পূর্ণ ফ্রিজ। আসল সমাধান ডিসপ্লে দমন নয়, ভারী কাজ ওয়ার্কার থ্রেডে সরিয়ে UI থ্রেডকে পাঁচ সেকেন্ড তো দূরের কথা এক দশমাংশ সেকেন্ডও ব্লক না করা। লক্ষ করুন, ডিবাগার লাগানো থাকলে OS ঘোস্ট উইন্ডো তৈরি করে না, তাই ডিবাগিংয়ে "Not Responding" কখনো হয় না বলে মনে হতে পারে।
- DoEvents দিয়ে (মেসেজ লুপ নিজে পাম্প করে) "Not Responding" এড়ানো গ্রহণযোগ্য?
- সুপারিশকৃত নয়। ভারী কাজের মাঝে DoEvents বা PeekMessage লুপ ঘোরালে হ্যাং-উইন্ডো বিচার এড়ানো যায়, কিন্তু যেকোনো ইভেন্ট হ্যান্ডলার তখন রি-এন্টার করতে পারে — বোতামের দ্বিতীয় ক্লিক, উইন্ডো বন্ধ, টাইমার ইত্যাদি। অন্য হ্যান্ডলার এখনও প্রক্রিয়াধীন ডেটা আবার লেখা, অথবা বন্ধ হওয়ার কথা ছিল এমন ফর্ম ছুঁয়ে থ্রো, সময়নির্ভর ও পুনরুৎপাদন-কঠিন রিএন্ট্রান্সি বাগ তৈরি করে — "Not Responding"-এর চেয়ে খারাপ। সঠিক পথ Task.Run ইত্যাদি দিয়ে কাজ নিজে ওয়ার্কার থ্রেডে সরানো, আর UI থ্রেডকে শুধু অগ্রগতি দেখানো ও বাতিল গ্রহণের দায়িত্বে রাখা।
- ওয়ার্কার থ্রেড থেকে UI (কন্ট্রোল) কীভাবে আপডেট করব?
- WinForms কন্ট্রোল ও WPF এলিমেন্ট শুধু যে থ্রেড তৈরি করেছে সেখান থেকেই ছোঁয়া যায় (সাধারণত UI থ্রেড)। ওয়ার্কার থ্রেড থেকে সরাসরি ছুঁলে এক্সেপশন বা অনির্ধারিত আচরণ হয়। C#-এ async/await সবচেয়ে সহজ পথ: await-এর পরের কন্টিনিউয়েশন কলিং UI থ্রেডে ফেরে, তাই await-এর পর কন্ট্রোল স্বাভাবিকভাবে আপডেট করা যায়। স্পষ্টভাবে বদলাতে WinForms-এ Control.Invoke/BeginInvoke ও WPF-এ Dispatcher.InvokeAsync ব্যবহার করুন। নেটিভ Win32-এ প্রতিষ্ঠিত আকৃতি ওয়ার্কার থ্রেড UI থ্রেডে কাস্টম সম্পূর্ণি মেসেজ PostMessage করে, আর উইন্ডো প্রসিডিউর UI আপডেট করে।
- অ্যাপ কেন "Not Responding" দেখাচ্ছে তা কীভাবে অনুসন্ধান করব?
- গুরুত্বপূর্ণ হলো হ্যাং "মুহূর্তটি"-র স্টেট ধরা। আগে Task Manager-এর Details ট্যাব থেকে "Create dump file" দিয়ে ফুল ডাম্প নিন, তারপর WinDbg-এ UI থ্রেডের (মেসেজ লুপ চালানো থ্রেড) স্ট্যাক দেখুন। সিঙ্ক্রোনাস I/O, নেটওয়ার্ক ওয়েট, লক ওয়েট, নাকি SendMessage দিয়ে অন্য থ্রেডের অপেক্ষা — স্ট্যাকেই দেখা যায়। লাইভ প্রক্রিয়া দেখতে Process Explorer-এর থ্রেড তালিকা ও স্ট্যাক ভিউ উপকারী; সময় ধরে অনুসরণ করতে WPR ট্রেস ধরা কার্যকর। এই সাইটের পরিচায়ক WinDbg নিবন্ধ, ব্যবহারিক Process Explorer, আর ব্যবহারিক WPR/WPAও দেখুন।