Win32 Thread Pool API — CreateThreadpoolWork से thread बनाए बिना concurrency
· अद्यतन तिथि: · Go Komura · Windows, multithreading, C++, Windows development, Win32 API, performance
संशोधन इतिहास (पहला संस्करण, 22 Aug 2026 को प्रकाशित)
- पहला प्रकाशन
इस लेख का हवाला दें(DOI: 10.5281/zenodo.22176807)
यह लेख Zenodo पर संग्रहीत है। नीचे वह DOI है जो हमेशा नवीनतम संस्करण की ओर ले जाता है, और वह DOI भी जो आपके पढ़े जा रहे संस्करण से बँधा है।
Go Komura (2026). Win32 Thread Pool API — CreateThreadpoolWork से thread बनाए बिना concurrency. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22176807 https://comcomponent.com/hi/blog/win32-thread-pool-api/
- DOI (नवीनतम संस्करण)
- 10.5281/zenodo.22176807
- DOI (यह संस्करण)
- 10.5281/zenodo.22176808
“Per client एक CreateThread।” “Timer के लिए एक।” “Event की wait के लिए एक।” — Native Windows code में threads इस तरह फैलते जाते हैं। हर thread stack और kernel object खर्च करता है, और बनाना-destroy करना भी लागत है। काम महीन है, thread भारी — उस mismatch को सोखने के लिए OS जो देता है वह thread pool है।
.NET के ThreadPool और Task.Run की सुविधा जानी-पहचानी है, पर वास्तव में Win32 native में भी अच्छी तरह design की गई, OS-standard thread-pool API है। Windows Vista में व्यापक redesign के बाद, यह API native concurrency की बुनियाद है: work, timer, wait और async I/O unified callback mechanism से सँभाल सकती है। C/C++ में Windows apps, services और DLL लिखने वाले developers के लिए, यह लेख इस API की structure व use, और जिनमें फँसना आसान है उन जालों को, primary sources पर आधारित समझाता है।
1. निष्कर्ष पहले
- बड़ी संख्या में short-lived काम जारी करने, और केवल-wait threads बदलने के लिए, thread pool अपने
CreateThreadसे बेहतर है। Thread management OS पर छोड़ते हैं और thread संख्या व context switch घटा सकते हैं।1 - जो use करना चाहिए वह नई API है (
CreateThreadpoolWorkfamily)। Vista redesign ने इसे पुरानी API (QueueUserWorkItemfamily) से सरल, अधिक reliable और high-performance बनाया, और एक process में कई स्वतंत्र pools भी बना सकते हैं।12 - चार प्रकार के objects हैं। work, जिसमें काम जमा करते हैं; timer, जो समय या अवधि पर चलता है; wait, जो kernel object signal होने पर चलता है; और io, जो async I/O complete होने पर चलता है। सभी एक ही callback mechanism पर सवार हैं।3
- Shutdown है “wait, फिर close”। चलते callbacks पीछे न छोड़ने का discipline —
WaitForThreadpoolWorkCallbacksfamily से completion की wait, या cleanup group से bulk सँभालना — आवश्यक है।4 - Callback के अंदर: लंबे समय block न करें (यदि करेंगे,
CallbackMayRunLong); उसी pool पर synchronous completion wait न करें; thread की state दूषित न करें। ये तीन iron rules हैं।56 - DLL से use: unload race से बचें। Explicit shutdown function में completion की wait करें, और
FreeLibraryWhenCallbackReturnsजैसी dedicated API जानें।3
2. Pool क्यों, और pool कब
Thread pool का विचार सरल है। Per काम thread बनाने के बजाय, OS द्वारा managed worker threads के समूह पर काम (callback) फेंकते हैं। Worker काम एक के बाद एक execute करते हैं, और OS संख्या को load के अनुसार adjust करता है।
Official documentation उन apps के ठोस प्रकार गिनता है जहाँ pool लाभ देता है।1
- Apps जो बड़ी संख्या में छोटे work items parallel जारी करते हैं (search, network I/O, आदि)
- Apps जो short-lived threads बार-बार बनाते और तोड़ते हैं
- Apps जो background में स्वतंत्र काम parallel process करते हैं
- Apps जो kernel object या event की wait के लिए dedicated threads रखते हैं
अंतिम बिंदु आसानी से छूट जाता है। यदि पाँच threads केवल इसलिए सोते हैं कि “event signal हो तो चलें”, उन्हें pool पर पाँच wait objects से बदला जा सकता है, और wait pool के waiter threads पर एकत्र हो जाती है।
इसके उल्टे, ऐसा काम भी है जो pool के अनुकूल नहीं। जिसे thread priority बदलनी हो, जिसे COM STA चाहिए, जो process के पूरे जीवन चलता रहे — जिस काम को thread पर “personality” चाहिए वह dedicated thread पर रहता है। Worker thread shared resource है; उधार लिया जाता है।
flowchart TB
accTitle: Dedicated thread और pool का चुनाव
accDescr: पहले जाँचें कि priority या STA जैसी thread personality चाहिए या नहीं, और क्या लंबे समय चलता है; जो न हो वह short-lived, high-volume या wait-style काम thread pool पर जाता है
q1{"Priority या STA जैसी personality?"} -->|"हाँ"| ded["Dedicated thread पर रखें"]
q1 -->|"नहीं"| q2{"लंबे समय चलता है?"}
q2 -->|"हाँ"| ded
q2 -->|"नहीं"| pool["Thread pool पर रखें"]
pool -.-> ex["Short-lived काम, wait, timer, I/O"]
चित्र 1: Pool पर केवल वही काम रख सकते हैं जिसे “personality नहीं चाहिए और जो जल्दी खत्म हो”। बाकी पहले की तरह dedicated thread पर रहता है।
इतिहास का एक बिंदु भी पक्का करें। Thread-pool API की दो पीढ़ियाँ हैं। Windows 2000 से चली पुरानी API (QueueUserWorkItem, RegisterWaitForSingleObject आदि), और Vista में व्यापक redesign की नई API (CreateThreadpoolWork family)। नई API worker thread के प्रकार एक करती है, dedicated persistent threads, एक process में कई pools, cleanup group आदि देती है, और official documentation साफ़ कहता है कि यह “सरल, अधिक reliable, बेहतर performance, अधिक flexible” है।1 पुरानी API में “queue में जाने के बाद काम cancel नहीं कर सकते” जैसी structural constraints भी हैं।2 यहाँ से यह लेख केवल नई API सँभालता है।
flowchart TB
accTitle: पुरानी thread-pool API और नई API का मेल
accDescr: पुरानी API का QueueUserWorkItem नई API के work object से, timer queues timer से, registered waits wait से, और BindIoCompletionCallback io से मेल खाते हैं
o1["QueueUserWorkItem"] --> n1["work"]
o2["Timer queues"] --> n2["timer"]
o5["Registered waits"] --> n3["wait"]
o4["BindIoCompletionCallback"] --> n4["io"]
चित्र 2: पुरानी API से migration target एक-से-एक है। मौजूदा code की सूची इस मेल से शुरू हो सकती है।
3. चार objects — work, timer, wait और io
नई API के केंद्र में चार प्रकार के objects हैं जिनकी callback-चलने की शर्तें अलग हैं।3
| Object | Create function | Callback कब चलता है |
|---|---|---|
| work | CreateThreadpoolWork | SubmitThreadpoolWork से जमा करने पर |
| timer | CreateThreadpoolTimer | निर्दिष्ट समय या अवधि आने पर |
| wait | CreateThreadpoolWait | Kernel object signal होने पर |
| io | CreateThreadpoolIo | जुड़े handle पर async I/O complete होने पर |
flowchart TB
accTitle: Thread pool के चार objects और callback mechanism
accDescr: work स्पष्ट जमा पर चलता है, timer समय पर, wait kernel-object signal पर, और io async I/O completion पर; सभी एक ही worker thread समूह पर callback के रूप में चलते हैं
kind{"कौन सा object?"}
kind --> w["work(जमा पर)"]
kind --> more{"Timer, wait, या io?"}
more --> t["timer(समय / अवधि)"]
more --> rest{"Wait या io?"}
rest --> wt["wait(signal पर)"]
rest --> io["io(I/O completion)"]
w --> pool["Worker callbacks चलाते हैं"]
t --> pool
wt --> pool
io --> pool
चित्र 3: चलने की शर्तें अलग हैं, पर चारों इस mechanism में एक हैं कि “उसी pool के workers callbacks चलाते हैं”।
यह एकीकरण practical शक्ति है। Periodic processing, event प्रतिक्रिया और I/O-completion processing प्रत्येक को dedicated thread पर लिखने के बजाय, उन्हें एक callback style पर संरेखित कर सकते हैं। Timers पूरे pool की एक timer queue में एकत्र होते हैं, और wait थोड़े waiter threads पर एकत्र होती है — process से “केवल सोने वाले” threads गायब हो जाते हैं।1
flowchart TB
accTitle: केवल-wait threads को wait object से बदलना
accDescr: पहले per event सोने वाले केवल-wait threads wait objects बन जाते हैं और pool के waiter threads पर एकत्र होते हैं, इसलिए callback केवल signal होने पर चलता है
old2["5 dedicated waiter threads अलग सोते"] -.-> waste["5 stacks और 5 threads खर्च"]
new2["5 wait objects"] --> agg["Pool के waiter threads पर एकत्र"]
agg --> cb2["Callback केवल signal पर"]
चित्र 4: “केवल सोकर wait” वाले threads wait objects बनाकर हटाए जा सकते हैं। Pool migration की यह स्पष्ट पहली चाल है।
4. बुनियादी pattern — work object से एक पूरा चक्कर
सबसे अधिक use होने वाले work object से शिष्टाचार एक बार चलते हैं।4
VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PTP_WORK work)
{
// The context is fixed at creation time. Per-item data is passed through a synchronised queue
WORK_QUEUE* queue = (WORK_QUEUE*)context;
ITEM* item = Dequeue(queue); // Take one item under exclusive control
ProcessItem(item);
}
// 1) Create (bind the callback to the shared context = the queue)
PTP_WORK work = CreateThreadpoolWork(WorkCallback, &queue, NULL);
if (!work) { /* Failure handling with GetLastError */ }
// 2) Submit once for each item you enqueue (keep the item count and the submit count in step)
Enqueue(&queue, item);
SubmitThreadpoolWork(work);
// 3) Stop the submitter, then wait for completion (TRUE also attempts to cancel work that has not yet started)
WaitForThreadpoolWorkCallbacks(work, FALSE);
// 4) Close
CloseThreadpoolWork(work);
दो बिंदु पकड़ें। पहला, एक ही work object पर SubmitThreadpoolWork एक से अधिक बार कर सकते हैं। हर जमा callback चलाता है (parallel)।7 पर callback को दिया गया context create time पर तय होता है, इसलिए एक work object से “एक ही तरह के काम की N वस्तुएँ” लिखते समय, ऊपर के code की तरह context में synchronized queue रखें और per जमा एक वस्तु लें (per वस्तु work object बनाने वाला design भी ठीक है)। दूसरा, बंद करने से पहले हमेशा completion की wait करें। चलते या queued callbacks रहते object बंद करना, या callback जिस memory का reference ले उसे free करना, ज्यों का त्यों use-after-free है। इस wait को सुरक्षित close बनाने के लिए पहले submitter रोकना prerequisite है — ऐसी structure में जहाँ दूसरा thread wait के parallel अभी Submit कर सकता हो, wait के बाद का जमा Close से race करता है। WaitForThreadpoolWorkCallbacks का दूसरा argument TRUE देने से अभी start न हुए जमा cancel करने का प्रयास भी होता है।
flowchart TB
accTitle: Work object का lifecycle
accDescr: CreateThreadpoolWork से बनाएँ; SubmitThreadpoolWork से जमा करें और callbacks parallel चलें। Shutdown पर पहले नए जमा रोकें, WaitForThreadpoolWorkCallbacks से हर callback की completion की wait करें, फिर CloseThreadpoolWork से बंद करें
c["CreateThreadpoolWork से बनाएँ"] --> s["SubmitThreadpoolWork से जमा(दोहराया जा सकता)"]
s --> run["Callbacks parallel चलते हैं"]
run --> stop3["नए जमा रोकें"]
stop3 --> w["WaitForThreadpoolWorkCallbacks से wait"]
w --> cl["CloseThreadpoolWork से बंद करें"]
चित्र 5: Shutdown order है “जमा रोकें → completion की wait → close”। कोई छोड़ें तो use-after-free या race मिलती है।
Default पर callbacks process-default pool पर चलते हैं। कई uses के लिए वह पर्याप्त है। अगला अध्याय तब है जब pool बाँटना चाहें।
5. Custom pool और cleanup group
Pool बाँटें। CreateThreadpool से स्वतंत्र pool बना सकते हैं और SetThreadpoolThreadMaximum / SetThreadpoolThreadMinimum से thread संख्या की ऊपरी व निचली सीमा set कर सकते हैं।8 Typical use isolation है। ताकि “धीमा हो सकने वाला batch-जैसा काम” “तुरंत प्रतिक्रिया चाहिए वाले काम” के workers न खा जाए, pool बाँटें और प्रत्येक को अपना thread budget दें।
Callback environment से बाँधें। काम किस pool पर चले यह TP_CALLBACK_ENVIRON (callback environment) initialize कर, SetThreadpoolCallbackPool से pool की ओर इंगित कर, और उसे CreateThreadpoolWork आदि के तीसरे argument के रूप में देकर specify होता है।9
Cleanup group से मोड़ें। कई objects बनाने वाले module में shutdown processing “सभी की wait, सभी close” का पाठ बन जाती है। यदि CreateThreadpoolCleanupGroup से group बनाएँ और callback environment से हर object उससे जोड़ें, तो एक CloseThreadpoolCleanupGroupMembers हर member object की completion-wait और release एक साथ करता है।34
flowchart TB
accTitle: Callback environment से configuration बाँधना
accDescr: Callback environment custom pool और cleanup group की ओर इंगित करता है; उस environment से बने work और timer उसी pool पर चलते हैं, और cleanup group पर bulk operation completion-wait व release एकत्र करता है
env["Callback environment(TP_CALLBACK_ENVIRON)"] --> cp["Custom pool(thread संख्या नियंत्रण)"]
env --> cg["Cleanup group"]
env --> obj["work / timer / wait / io बनाते समय"]
cg -.-> close["एक झटके में wait और release"]
चित्र 6: Callback environment वह mechanism है जो object-create time पर “किस pool पर चले, और कौन साफ़ करे” inject करता है।
6. जाल — callback के अंदर discipline
लगभग हर thread-pool bug “उधार thread पर मनमानी करने” से आता है।
लंबे समय block करना। Pool thread संख्या इस धारणा पर adjust करता है कि callbacks तुरंत return करें। Default पर लंबा काम या लंबी wait अन्य callbacks के execution में delay करती है। लंबे चल सकने वाले callback को CallbackMayRunLong से “यह लंबा चलेगा” घोषित करना चाहिए (pool इसे thread जोड़ने का संकेत मानता है), या पहले से dedicated thread पर भेजना चाहिए। ध्यान दें कि CallbackMayRunLong FALSE लौटाता है जब अन्य callbacks के लिए worker तैयार नहीं कर सकता। Return value जाँचे बिना block करते रहें तो pool फिर भी जाम होता है, इसलिए FALSE हो तो उस पक्ष पर जाएँ जो block न करे — काम बाँटें, dedicated thread पर भेजें, आदि।5
उसी pool पर synchronous completion wait। ऐसा रूप जहाँ callback A के अंदर उसी pool पर जमा काम B की completion की WaitForThreadpoolWorkCallbacks आदि से wait हो, उस क्षण pool-starvation deadlock बन जाता है जब हर worker “दूसरे worker की wait” में हो। कामों के बीच dependency को wait नहीं, बल्कि “B के completion callback से अगला जमा करें” जैसी continuation के रूप में लिखें।
flowchart TB
accTitle: Pool-starvation deadlock की structure
accDescr: यदि हर worker thread उसी pool पर जमा अन्य काम की completion की synchronous wait करे, तो उस काम को चलाने के लिए खाली worker नहीं बचता, और सब सदा wait करते रहते हैं
w1["Worker 1: काम X की wait"] --> q["काम X और Y चलने की wait"]
w2["Worker 2: काम Y की wait"] --> q
q -.-> none["उन्हें चलाने को खाली worker नहीं"]
none -.-> dead["सब सदा wait(starvation deadlock)"]
चित्र 7: यदि worker के अंदर से worker की synchronous wait करें, तो waited काम चलाने वाला कोई नहीं बचता।
Thread की state दूषित करना। Worker thread अगले callback के लिए reuse होता है। Thread priority बदलना, COM initialization state, TLS में छोड़ा मान, छोड़ना भूल गया lock — इनमें से कोई भी अगले (असंबंधित) callback का दूषण बनता है। “Pool पर फेंकी गई function thread की personality पर depend न हो” पुरानी-API काल से official सावधानी है।6 Cleanup के dedicated mechanisms हैं; उदाहरण के लिए, LeaveCriticalSectionWhenCallbackReturns pool से कह सकता है कि “यह callback return करे तो यह lock छोड़ दें”।3
DLL unload से race। Callback चलते code वाला DLL unload हो तो access violation होता है। बुनियादी रूप है DLL की shutdown function में completion की पूरी wait; FreeLibraryWhenCallbackReturns उस स्थिति के लिए है “यह callback अंतिम काम है, और पूरा होने पर मैं खुद सहित DLL free करना चाहता हूँ”। पर यह API केवल “चलते callback के return पर एक reference छोड़ती है”; callback start होने से पहले unload नहीं रोकती। इसे जोड़ के रूप में use करें: जमा से पहले GetModuleHandleEx से अपना module reference लें, और callback उस reference को इस API से छोड़े।3 और यह completion-wait DllMain के अंदर न करें — “DllMain और Loader Lock” में कहा गया है, DllMain के अंदर दूसरे thread की wait deadlock pattern है।
flowchart TB
accTitle: DLL unload और callback की race की तैयारी
accDescr: Callback चलते DLL unload access violation बनता है, इसलिए बुनियादी रूप explicit shutdown function में completion की wait कर फिर close करना है; जब अंतिम callback खुद DLL free करे, FreeLibraryWhenCallbackReturns use करें
risk["Callback के दौरान unload"] -.-> av["Access violation"]
g1["Wait, फिर close"] --> safe["सुरक्षित unload"]
g1 -.-> g1N["Shutdown function में"]
g2["FreeLibraryWhenCallbackReturns"] --> safe
g2 -.-> g2n["अंतिम callback DLL free करे"]
g1 -.-> ng["DllMain के अंदर नहीं"]
av ~~~ g1
चित्र 8: बुनियादी रूप explicit shutdown function में “wait, फिर close” करना है। DllMain के अंदर wait अलग deadlock बुलाती है।
जमा काम के अंदर exception और crash। Worker thread पर unhandled exception process साथ ले जाता है। Dedicated thread की thread function की तरह, callback के प्रवेश पर exception व्यापक रूप से पकड़ने और log करने की नीति लागू करें।
7. Standard library और .NET से संबंध — किस परत पर लिखें
अंत में, अन्य tools से यह कहाँ बैठता है यह छाँटते हैं।
- यदि C++
std::async/std::threadपर्याप्त हों, वे पहला उम्मीदवार हैं। वे portable हैं, code छोटा है, औरfutureके अर्थ भी standard तय करते हैं।10 - Win32 thread pool सीधे use करने के कारण तब हैं जब आप (1) timer, wait और io सहित unified callback mechanism चाहते हैं, (2) pool विभाजन या thread-संख्या नियंत्रण चाहते हैं, या (3) DLL या COM component के अंदर अपने threads नहीं रखना चाहते।
- .NET पक्ष पर,
ThreadPoolऔरTaskवही भूमिका निभाते हैं, और I/O completion IOCP से बँधी है। यह तहखाने की structure “IOCP और .NET Thread Pool” में समझाई गई है।
flowchart TB
accTitle: किस परत के tools से लिखें यह तय करना
accDescr: यदि standard C++ async या thread पर्याप्त हों तो वे use करें; Win32 thread pool सीधे तब जब timer, wait और I/O completion का एकीकरण, pool विभाजन या thread-संख्या नियंत्रण चाहिए, या DLL या COM में अपने threads न चाहें
q1{"Standard C++ tools पर्याप्त?"} -->|"हाँ"| std["std::async / std::thread"]
q1 -->|"नहीं"| q2{"क्या चाहिए?"}
q2 -->|"timer, wait और io का एकीकरण"| tp["Win32 thread pool"]
q2 -->|"Pool विभाजन / संख्या नियंत्रण"| tp
q2 -->|"DLL में अपने threads से बचना"| tp
चित्र 9: संदेह हो तो standard library से शुरू करें; इस API की बारी तब आती है जब वह जो व्यक्त न कर सके वह requirement आए।
यानी, यह API concurrency की बुनियाद उस बिंदु पर है जब आपने “native लिखना” तय कर लिया हो। Realistic cleanup चरणबद्ध है: अपने CreateThread के फैलाव से migration target के रूप में, पहले work object लाएँ, फिर केवल-wait threads को wait और timer threads को timer से बदलें।
8. सारांश
- बड़ी संख्या में short-lived काम जारी करने, और केवल-wait व केवल-timer threads साफ़ करने के लिए, अपने threads के बजाय OS-standard thread pool। Use Vista के बाद की नई API है।
- केंद्र में चार objects work, timer, wait और io हैं। चलने की शर्तें अलग; वे एक ही worker समूह और एक ही callback style पर एक हैं।
- शिष्टाचार है “बनाएँ → जमा करें → completion की wait → close”। कई जमा parallel चलते हैं। Cleanup group shutdown processing bulk कर सकता है।
- Callback के तीन iron rules: लंबे समय block न करें (यदि करेंगे,
CallbackMayRunLong); उसी pool पर synchronous wait न करें; thread की state दूषित न करें। - DLL से use: unload race से बचें। Explicit shutdown function में completion की wait करें;
DllMainमें न करें। - जहाँ standard C++ या .NET पर्याप्त हों, वे use करें। इस API की बारी तब है जब timer/wait/io का एकीकरण या pool नियंत्रण चाहिए।
Thread-pool API, Win32 API में, नई और बेहतर-design वाली ओर है। “Thread बनाने” के विचार से “callback फेंकने” के विचार तक स्थानांतरण पूरा होने पर, native code में concurrency काफी साफ़ लिखी जाती है।
संबंधित लेख
- Windows I/O की गहराई (भाग 3) — I/O Completion Ports (IOCP) और .NET Thread Pool: async/await के नीचे की तह
- व्यावहारिक multithreading best practices: C++ संस्करण — RAII और jthread से structure द्वारा accidents हटाना
- व्यावहारिक multithreading best practices: C संस्करण — Win32 API तरीके से सुरक्षित लिखना
- Spurious wakeup — Condition Variable “बिना notification के” क्यों जागती है और Windows पर सही wait
- DllMain और Loader Lock — DLL initialization में “कुछ मत करो” कहा जाने का असली कारण
- Windows पर Sleep(1) के बजाय Event Wait क्यों चुनें
संबंधित परामर्श क्षेत्र
KomuraSoft LLC उन native code से thread pool पर migration design सँभालता है जिनके threads फैल गए हैं, C++ apps और DLL में concurrent processing की design review, और pool starvation या callback से आए hang व crash की root-cause जाँच। मौजूदा code की सूची से परामर्श स्वागत योग्य है।
संदर्भ लिंक
-
Microsoft Learn, Thread Pools. Thread pool worker threads का संग्रह होने पर जो app की ओर से async callbacks दक्षता से execute करता है; जिन app प्रकारों के अनुकूल है (बड़ी संख्या में छोटे work items parallel जारी करना, short-lived threads बार-बार बनाना-तोड़ना, स्वतंत्र काम parallel process करना, kernel object पर विशेष wait, आदि); और व्यापक Vista redesign (worker-thread प्रकारों का एकीकरण, एक timer queue, dedicated persistent threads, cleanup group, एक process में कई pools, और नई API) पर। ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Thread Pooling. पुरानी thread-pool API की structure पर (QueueUserWorkItem, timer queues, registered waits, BindIoCompletionCallback); queue में जाने के बाद काम cancel करने का तरीका न होने पर; और Vista में आई नई thread-pool API के सरल तथा reliability, performance व flexibility में श्रेष्ठ बताए जाने पर। ↩ ↩2
-
Microsoft Learn, threadpoolapiset.h header. चार object-create functions CreateThreadpoolWork, CreateThreadpoolTimer, CreateThreadpoolWait और CreateThreadpoolIo सहित function list पर; cleanup group (CreateThreadpoolCleanupGroup); और callback completion से बँधे cleanup (LeaveCriticalSectionWhenCallbackReturns, FreeLibraryWhenCallbackReturns, आदि) पर। ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Using the Thread Pool Functions. CreateThreadpoolWork से बनाने, SubmitThreadpoolWork से जमा करने, WaitForThreadpoolWorkCallbacks से completion की wait, और CloseThreadpoolWork से बंद करने की बुनियादी प्रक्रिया पर; और custom pool को callback environment व cleanup group से जोड़ने वाले configuration उदाहरण पर। ↩ ↩2 ↩3
-
Microsoft Learn, CallbackMayRunLong function (threadpoolapiset.h). Pool को सूचित करने पर कि वर्तमान callback लंबे समय चल सकता है, ताकि pool अन्य callbacks के लिए thread सुरक्षित करने का निर्णय ले सके; और जहाँ संभव हो लंबे-चलने वाले callbacks के लिए dedicated thread पर विचार करने पर। ↩ ↩2
-
Microsoft Learn, Thread Pooling. Thread pool पर जमा work items, और वे जिन functions को call करते हैं, के thread-pool safe होने पर; execution thread को dedicated persistent thread न मानने पर; और TLS तथा persistent thread चाहिए वाली async calls से बचने पर। ↩ ↩2
-
Microsoft Learn, SubmitThreadpoolWork function (threadpoolapiset.h). एक ही work object को पूर्ववर्ती callback की completion की wait किए बिना एक से अधिक बार जमा कर सकने पर, जिससे callbacks parallel चलें; और pool के दक्षता के लिए thread संख्या adjust (throttle) कर सकने पर। ↩
-
Microsoft Learn, SetThreadpoolThreadMaximum function (threadpoolapiset.h). CreateThreadpool से बने pool की worker-thread संख्या पर ऊपरी सीमा set कर सकने पर (निचली सीमा SetThreadpoolThreadMinimum है)। ↩
-
Microsoft Learn, CreateThreadpoolWork function (threadpoolapiset.h). Callback function और context pointer से work object बनाने पर; और तीसरे argument TP_CALLBACK_ENVIRON के callback का execution environment (संबंधित pool आदि) specify कर सकने पर, NULL का अर्थ default environment में चलना। ↩
-
Microsoft Learn, <future>. std::async और future के माध्यम से per कार्य async execution standard library के रूप में दिए जाने पर, ताकि threads सीधे manage किए बिना concurrency लिख सकें। ↩
संबंधित लेख
निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।
DllMain और Loader Lock — DLL initialization में "कुछ मत करो" कहा जाने का असली कारण
DllMain से LoadLibrary क्यों न call करें और दूसरे thread से synchronize न करें। Primary sources से यह लेख बताता है कि loader lock हर DLL ...
Named Pipes व्यवहार में — design से security तक, Windows की standard IPC
Named pipes — Windows की standard inter-process communication — की practical guide। यह लेख primary sources से byte और message mode का चुन...
"Not Responding" वास्तव में क्या है — Windows ऐप के hang का फैसला कैसे करता है, और hang न करने वाले ऐप कैसे design करें
Windows का "Not Responding" वह mechanism है जिसमें OS तय करता है कि window ने 5 सेकंड message नहीं लिया और उसे ghost window से बदल देता ह...
Spurious wakeup — condition variable "बिना notification के" क्यों जागती है और Windows पर सही wait कैसे करें
Condition variable की wait notification आए बिना भी लौट सकती है (spurious wakeup)। यह लेख Windows implementation से समझाता है कि spec इसे ...
Sleep से resume पर टूटने वाले apps — Windows power events कैसे काम करते हैं और उनसे बचने वाले business apps कैसे बनाएँ
Laptop खोला और business app के connections मर चुके थे — कारण वह design है जिसने sleep का हिसाब ही नहीं रखा। यह लेख WM_POWERBROADCAST noti...
संबंधित विषय
ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।
Windows के तकनीकी विषय
Windows विकास, बग जाँच और मौजूदा संपत्तियों के उपयोग का प्रवेश-द्वार।
इस विषय से जुड़ी सेवाएँ
यह लेख निम्नलिखित सेवाओं से सीधे जुड़ा है।
Windows ऐप विकास
व्यावसायिक ऐप, डिवाइस एकीकरण और संचार उपकरण, आवश्यकताओं से विकास तक।
अक्सर पूछे जाने वाले प्रश्न
इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।
- CreateThread से अपने threads बनाने की तुलना में thread pool बेहतर क्यों है?
- जब बड़ी संख्या में short-lived काम हों तो दक्षता, और thread-management code में कमी। Thread बनाना और destroy करना अनदेखा न करने योग्य लागत है, इसलिए जो app "हर काम के लिए CreateThread और पूरा होने पर destroy" दोहराता है, या केवल event की wait में सोने वाले कई threads रखता है, वह pool पर जाने से thread संख्या और context switch घटा सकता है। Official documentation pool उम्मीदवारों में उन apps को गिनता है जो बड़ी संख्या में छोटे work items parallel जारी करते हैं, जो कई short-lived threads बनाते हैं, और जिनके threads केवल kernel object की wait के लिए dedicated हैं। इसके उल्टे, वह काम जिसे "thread पर अपनी personality चाहिए" — priority बदलना, COM STA, लंबे समय चलने वाली dedicated processing — पहले की तरह dedicated thread पर ही रखना चाहिए।
- QueueUserWorkItem जैसी पुरानी thread-pool functions से यह कैसे अलग है?
- Thread pool Windows Vista में व्यापक रूप से redesign हुआ। वर्तमान threadpoolapiset-family API (CreateThreadpoolWork आदि) नई API हैं; QueueUserWorkItem, RegisterWaitForSingleObject आदि पुरानी (legacy) API हैं। नई API worker thread के प्रकार एक करती है, एक process में कई स्वतंत्र pools बनाने देती है, और cleanup group से bulk release तथा callback completion से बँधे lock release या DLL unload जैसे mechanisms देती है। Official documentation यह भी कहता है कि नई API सरल है और reliability, performance व flexibility में श्रेष्ठ है। पुरानी API में structural constraints भी हैं, जैसे "queue में जाने के बाद काम cancel करने का कोई तरीका नहीं", इसलिए नए code में नई API use करें।
- Callback के अंदर ऐसी बातें हैं जो नहीं करनी चाहिए?
- तीन बड़ी हैं। पहली, default पर लंबे समय block करना या लंबा काम करना। Pool thread संख्या इस धारणा पर adjust करता है कि callbacks तुरंत पूरे हों, इसलिए लंबे काम के लिए या तो CallbackMayRunLong से घोषित करें या dedicated thread use करें। दूसरी, उसी pool पर जमा किए अन्य काम की completion की synchronous wait। यदि हर worker "दूसरे worker की wait" में फँस जाए, तो pool-starvation deadlock होता है। तीसरी, thread की personality पर dependency। Worker threads callbacks के बीच shared होते हैं, इसलिए बदली thread priority या COM initialization state लेकर लौटना, या TLS में state छोड़ना, अगले callback को दूषित करता है। अंत में cleanup (lock छोड़ना या DLL unload) के लिए LeaveCriticalSectionWhenCallbackReturns और FreeLibraryWhenCallbackReturns जैसे dedicated mechanisms हैं।
- DLL से thread pool use करते समय क्या ध्यान रखें?
- सबसे बड़ा खतरा है "callback अभी चल रहा हो और DLL unload हो जाए"। Unload के बाद callback चले तो access violation होता है। DLL पक्ष को shutdown processing में अपने जारी callbacks की completion की विश्वसनीय wait करनी चाहिए — WaitForThreadpoolWorkCallbacks जैसी wait function से, या cleanup group पर CloseThreadpoolCleanupGroupMembers से — और तभी object बंद करें। पर DllMain के अंदर यह wait loader lock से interact कर deadlock कर सकती है, इसलिए नियम है इसे DllMain में नहीं, explicit shutdown function में करना। उस स्थिति के लिए जब callback खुद DLL free करना चाहे क्योंकि "यह काम अंतिम है", dedicated API FreeLibraryWhenCallbackReturns दी गई है।
- अब C++ std::async और .NET का ThreadPool होने पर भी इस API को सीधे use करने के अवसर हैं?
- हाँ। कसौटी है "क्या उस परत का tool पर्याप्त है"। यदि C++ में चाहिए concurrency की granularity std::async या std::thread से ढक जाए, तो portability की दृष्टि से भी standard library पहला उम्मीदवार है। दूसरी ओर, timer, kernel-object wait और async I/O completion को एक callback mechanism में एक करना; काम के प्रकार के अनुसार pool बाँटना और thread संख्या नियंत्रित करना; DLL या COM component के अंदर अपने threads न रखना — ये requirements Win32 thread pool पूरा करता है। .NET के ThreadPool और IOCP से संबंध संबंधित लेख में है, और जब तक native लिख रहे हैं, नीचे की परत पर बैठा यह mechanism जानना व्यर्थ नहीं।