واجهة مجمع مؤشّرات الترابط Win32 ── تزامن دون إنشاء مؤشّرات ترابط، عبر CreateThreadpoolWork
· آخر تحديث: · غو كومورا · Windows, تعدّد مؤشّرات الترابط, C++, تطوير Windows, Win32 API, تحسين الأداء
سجل التعديلات (النسخة الأولى، نُشرت في 22 Aug، 2026)
- النشر الأول
الاستشهاد بهذا المقال(DOI (الأرشيف المسجّل): 10.5281/zenodo.22176793)
تشير معرّفات DOI أدناه إلى إصدارات مؤرشفة سابقًا، وقد تختلف عن النص الحالي. للإشارة إلى النص الحالي، استخدم رابط هذه الصفحة.
غو كومورا (2026). واجهة مجمع مؤشّرات الترابط Win32 ── تزامن دون إنشاء مؤشّرات ترابط، عبر CreateThreadpoolWork. شركة كومورا سوفت ذ.م.م.. https://comcomponent.com/ar/blog/win32-thread-pool-api/
- DOI (الأرشيف المسجّل)
- 10.5281/zenodo.22176793
- DOI (آخر إصدار مسجّل)
- 10.5281/zenodo.22241138
«CreateThread واحد لكلّ عميل.» «مؤشّر ترابط واحد للمؤقِّت.» «واحد لانتظار حدث.» في شيفرة Windows الأصليّة، المنعكس إضافة مؤشّر ترابط لكلّ عمل صغير. يستهلك كلّ مؤشّر ترابط مكدّساً وكائن نواة، وإنشاؤه وإتلافه يكلّفان أيضاً.
ما يمتصّ هذا عدم التطابق هو واجهة مجمع مؤشّرات الترابط Win32 القياسيّة في نظام التشغيل. يسلِّم التطبيق المعالجة التي يريد تشغيلها ردّ نداء ويترك إدارة مؤشّرات الترابط العاملة لنظام التشغيل. «عدم إنشاء مؤشّرات ترابط» لا يعني أنّ مؤشّرات الترابط تصير غير لازمة؛ يعني أنّ التطبيق لا ينشئ واحداً ويتلفه بنفسه لكلّ عمل.1
المقال موجَّه إلى المطوِّرين الذين يكتبون تطبيقات Windows وخدمات وDLLs بلغة C/C++، ويسير بترتيب اختيار الأداة → اختيار الكائن → تنفيذ العمل → تحذيرات ردود النداء والإيقاف. الموضوع عائلة واجهات CreateThreadpoolWork التي أُعيد تصميمها في Windows Vista. الشيفرة مقتطف يعرض هيكل الاستخدام؛ أجزاء خاصّة بالتطبيق كالطابور محذوفة.
1. الخلاصة أوّلاً
إن عالجت عدداً كبيراً من الأعمال قصيرة العمر، أدِر الأعمال لا مؤشّرات الترابط. غير أنّ التطبيق ما يزال يصمِّم متى يتوقّف عمل ومتى يمكن تحريره.
ثلاثة محاور للحكم.
- اختر الأداة. إن كفت أدوات C++ القياسيّة أو .NET، استخدمها. دور هذه الواجهة يأتي عندما تريد توحيد مؤقِّتات وانتظارات واكتمال إدخال/إخراج في Win32، أو عندما تريد تقسيم المجمّعات.
- سلِّم العمل بوحدات أعمال. استخدم work وtimer وwait وio في الواجهة الجديدة، واترك الأعمال التي تحتاج حالة خاصّة بمؤشّر الترابط، كأولويّة مؤشّر ترابط أو COM STA، على مؤشّرات ترابط مخصَّصة.
- ابنِ الإيقاف جزءاً من المجموعة. الشكل الأساسي «توقّف عن التقديم → انتظر الاكتمال → أغلق». داخل ردّ نداء، لا تحجب طويلاً، ولا تنتظر عملاً على المجمع نفسه بصورة متزامنة، وأعد حالة مؤشّر الترابط.
إن أردت تشغيل شيء أوّلاً، ابدأ بمثال work في الفصل 4 ثمّ افحص انضباط ردّ النداء في الفصل 5. الإيقاف بالجملة لعدّة كائنات مشمول في الفصل 6، وإلغاء تحميل DLL في الفصل 7.
2. الاختيار بين مجمع ومؤشّر ترابط مخصَّص والمكتبة القياسيّة
2.1 المجمع يناسب عملاً قصير العمر، عالي الحجم، محوره الانتظار
مجمع مؤشّرات الترابط مجموعة مؤشّرات ترابط عاملة يديرها نظام التشغيل. ينفِّذ العاملون ردود النداء واحداً تلو الآخر، ويضبط نظام التشغيل عددهم وفق الحمل. بتسليم العمل الذي كنت تفعله بإنشاء مؤشّرات ترابطك وإتلافها، تقلِّل شيفرة الإدارة وكلفة الإنشاء والإتلاف كليهما.1
تسرد الوثائق الرسميّة مرشّحين تطبيقات تُصدر عدداً كبيراً من عناصر العمل الصغيرة على التوازي، وتطبيقات تنشئ وتتلف بكثرة مؤشّرات ترابط قصيرة العمر، وتطبيقات تعالج عملاً مستقلّاً على التوازي في الخلفيّة، وتطبيقات تملك مؤشّرات ترابط مخصَّصة تنتظر كائنات نواة أو أحداثاً. البحث وإدخال/إخراج الشبكة وتجميع مؤشّرات الترابط التي لا تفعل سوى الانتظار أمثلة نموذجيّة.1
من جهة أخرى، عمل يحتاج تغييراً في أولويّة مؤشّر الترابط، أو يحتاج COM STA، أو يظلّ يعمل طوال عمر العمليّة يبقى على مؤشّر ترابط مخصَّص. المعيار هل يكفي أن تعمل المعالجة، أم يحتاج مؤشّر الترابط نفسه «شخصيّة».
flowchart TB
accTitle: الاختيار بين مؤشّر ترابط مخصَّص ومجمع
accDescr: افحص أوّلاً هل يحتاج مؤشّر الترابط شخصيّة كأولويّة أو STA وهل يعمل طويلاً؛ العمل قصير العمر أو عالي الحجم أو أسلوب الانتظار الذي لا يطابق أياً منهما فقط يذهب إلى مجمع مؤشّرات الترابط
q1{"يحتاج شخصيّة كأولويّة أو STA؟"} -->|"نعم"| ded["أبقِه على مؤشّر ترابط مخصَّص"]
q1 -->|"لا"| q2{"يعمل طويلاً؟"}
q2 -->|"نعم"| ded
q2 -->|"لا"| pool["ضعه على مجمع مؤشّرات الترابط"]
pool -.-> ex["عمل قصير العمر، انتظارات، مؤقِّتات، اكتمالات إدخال/إخراج"]
الشكل 1: العمل الذي «لا يحتاج شخصيّة وينتهي سريعاً» فقط ينتمي إلى المجمع. كلّ ما سواه يبقى على مؤشّر ترابط مخصَّص كما كان.
2.2 اسأل أوّلاً هل يكفي C++ القياسي أو .NET
إن غطّت الحبيبات std::async أو std::thread في C++، فالمكتبة القياسيّة المرشّح الأوّل. قابلة للنقل، ويمكن معالجة سلوك std::async وfuture على أساس المعيار.2
أسباب استخدام مجمع مؤشّرات الترابط Win32 مباشرة متطلّبات مثل الرغبة في آليّة ردّ نداء موحَّدة تشمل timer وwait وio؛ أو الرغبة في مجمّعات منفصلة أو أعداد مؤشّرات ترابط لكلّ نوع عمل؛ أو عدم الرغبة في ملكيّة مؤشّرات ترابط داخل DLL أو مكوِّن COM. افصل سؤال هل تريد توازياً فحسب عن هل تحتاج تحكّماً خاصّاً بـ Windows.
flowchart TB
accTitle: تقرير أدوات أيّ طبقة تكتب بها
accDescr: إن كفت أدوات async أو thread في C++ القياسي فاستخدمها؛ استخدم مجمع مؤشّرات الترابط Win32 مباشرة عندما تحتاج توحيد مؤقِّتات وانتظارات واكتمال إدخال/إخراج، أو تقسيم مجمّعات أو التحكّم في الأعداد، أو تجنّب ملكيّة مؤشّرات ترابط داخل DLL أو مكوِّن COM
q1{"هل تكفي أدوات C++ القياسيّة؟"} -->|"نعم"| std["std::async / std::thread"]
q1 -->|"لا"| q2{"ماذا تحتاج؟"}
q2 -->|"توحيد timer وwait وio"| tp["مجمع مؤشّرات الترابط Win32"]
q2 -->|"تقسيم المجمّعات / التحكّم في الأعداد"| tp
q2 -->|"تجنّب مؤشّرات ترابط خاصّة في DLL"| tp
الشكل 2: عند الشكّ، ابدأ بالمكتبة القياسيّة؛ دور هذه الواجهة يأتي عندما يظهر متطلَّب لا تستطيع التعبير عنه.
في .NET، يؤدّي ThreadPool وTask الدور نفسه، واكتمال الإدخال/الإخراج مربوط بـ IOCP. لشرح مفصَّل للعلاقة، انظر مقالة IOCP ومجمع مؤشّرات الترابط في .NET. لا حاجة للنزول إلى واجهة Win32 حيث تكفي أدوات الطبقة فوقها.
2.3 في الشيفرة الجديدة، استخدم الواجهة الجديدة من Vista فصاعداً
جيلان لواجهة مجمع مؤشّرات الترابط Win32: الواجهة التراث التي استمرّت منذ Windows 2000، مثل QueueUserWorkItem وRegisterWaitForSingleObject، وعائلة CreateThreadpoolWork الجديدة التي أُعيد تصميمها بالكامل في Windows Vista.
وحَّدت الواجهة الجديدة أنواع مؤشّر الترابط العامل ووفَّرت طابور مؤقِّت واحداً، ومؤشّرات ترابط دائمة مخصَّصة، وعدّة مجمّعات مستقلّة داخل عمليّة، ومجموعات تنظيف. تستشهد الوثائق الرسميّة أيضاً ببساطة الواجهة الجديدة وموثوقيّتها وأدائها ومرونتها مزايا.13
للواجهة التراث قيد بنيوي أيضاً: لا توجد طريقة لإلغاء عمل بعد وضعه في الطابور. استخدم الواجهة الجديدة في الشيفرة الجديدة، وعند مراجعة شيفرة قائمة ابدأ من التقابل التالي.3
flowchart TB
accTitle: التقابل بين واجهة مجمع مؤشّرات الترابط التراث والواجهة الجديدة
accDescr: QueueUserWorkItem في الواجهة التراث يُستبدل بكائن work في الواجهة الجديدة، وطوابير المؤقِّت بـ timer، والانتظارات المسجَّلة بـ wait، وBindIoCompletionCallback بـ io
o1["QueueUserWorkItem"] --> n1["work"]
o2["طوابير المؤقِّت"] --> n2["timer"]
o5["انتظارات مسجَّلة"] --> n3["wait"]
o4["BindIoCompletionCallback"] --> n4["io"]
الشكل 3: هدف الترحيل من الواجهة التراث ثابت واحداً لواحد. جرد الشيفرة القائمة يمكن أن يبدأ من جدول التقابل هذا.
3. كيف يعمل ── أربعة كائنات بشروط إطلاق مختلفة
3.1 اختر بما ينبغي أن يُطلِق ردّ النداء
في مركز الواجهة الجديدة الأنواع الأربعة التالية. اختر لا بـ «ماذا تعالج» فحسب، بل بـ ما ينبغي أن يُطلِق ردّ النداء.4
| الكائن | دالّة الإنشاء | شرط إطلاق ردّ النداء |
|---|---|---|
| work | CreateThreadpoolWork |
عندما يُقدَّم بـ SubmitThreadpoolWork |
| timer | CreateThreadpoolTimer |
عندما يصل الزمن أو الفترة المحدَّدان |
| wait | CreateThreadpoolWait |
عندما يصير كائن نواة مؤشَّراً |
| io | CreateThreadpoolIo |
عندما يكتمل إدخال/إخراج غير متزامن على المقبض المرتبط |
تختلف شروط الإطلاق، لكنّ عاملي المجمع نفسه يفعلون التنفيذ. بدل كتابة معالجة دوريّة واستجابة حدث ومعالجة اكتمال إدخال/إخراج كلّاً على مؤشّر ترابط مخصَّص، يمكنك محاذاتها على آليّة ردّ نداء مشتركة.
flowchart TB
accTitle: فصل شروط الإطلاق عن تنفيذ ردّ النداء
accDescr: ينفِّذ عاملو المجمع ردود نداء جعلتها كائنات بشروط إطلاق جاهزة، ويُستخدَم عامل أنهى أيضاً لردّ النداء التالي، لذا لا ينشئ التطبيق مؤشّر ترابط لكلّ عمل
app["التطبيق يحدِّد العمل وشرط إطلاقه"] --> obj["الكائن ينتظر الشرط"]
obj --> ready["يصير ردّ النداء جاهزاً للتشغيل"]
ready --> workers["عاملو المجمع ينفِّذونه"]
workers --> done["ينهي ويعيد العامل"]
done --> next["يُعاد استخدامه لردّ النداء التالي أيضاً"]
الشكل 4: فصل الآليّة التي تنتظر محفِّزاً عن العاملين الذين ينفِّذون المعالجة يقلِّل إدارة مؤشّرات الترابط لكلّ عمل.
3.2 «مؤشّرات الترابط التي لا تفعل سوى النوم والانتظار» يمكن استبدالها أيضاً
فائدة المجمع لا تقتصر على عمل مرتبط بالمعالج. تُجمَّع المؤقِّتات في طابور مؤقِّت واحد، وانتظارات مقابض متعدّدة على عدد صغير من مؤشّرات ترابط الانتظار.1
مثلاً، إن كان لديك خمسة مؤشّرات ترابط تنام فقط كي تستطيع «العمل عندما يُؤشَّر الحدث»، يمكنك التفكير في استبدالها بخمسة كائنات wait. بدل إمساك مؤشّر ترابط ينام بمفرده لكلّ واحد، تشغِّل معالجة الإشارة ردّ نداء.
flowchart TB
accTitle: استبدال مؤشّرات الترابط التي لا تفعل سوى الانتظار بكائنات wait
accDescr: مؤشّرات الترابط التي لا تفعل سوى الانتظار والتي نامت واحداً لكلّ حدث تُجمَّع على مؤشّرات ترابط انتظار المجمع عندما تُستبدل بكائنات wait، ويعمل ردّ النداء فقط عند الإشارة
old2["5 مؤشّرات ترابط لا تفعل سوى الانتظار تنام فرديّاً"] -.-> waste["تستهلك 5 مكدّسات و5 مؤشّرات ترابط"]
new2["5 كائنات wait"] --> agg["تُجمَّع على مؤشّرات ترابط انتظار المجمع"]
agg --> cb2["يعمل ردّ النداء فقط عند الإشارة"]
الشكل 5: «مؤشّرات الترابط التي لا تفعل سوى النوم والانتظار» يمكن حذفها بتحويلها إلى كائنات wait. هذه أوضح خطوة أولى في ترحيل مجمع.
4. أساسيّات التنفيذ ── أنشئ work وقدِّمه وأوقفه بأمان
4.1 أوّلاً، ذهاب وإياب واحد من الإنشاء إلى الإيقاف
سر في الاستخدام مرّة مع الكائن الأكثر أساسيّة، work. يربط CreateThreadpoolWork ردّ النداء بسياق، ويطلب SubmitThreadpoolWork التنفيذ. إن كان المعامل الثالث NULL، يُستخدَم مجمع العمليّة الافتراضي. كثير من الاستخدامات يناسبها هذا المجمع الافتراضي.56
التالي مقتطف يعرض الإجراء، لا برنامجاً كاملاً يُترجَم كما هو. WORK_QUEUE وITEM وEnqueue وDequeue وProcessItem تمثِّل معالجة جانب التطبيق. نفِّذ استبعاد تداخل الطابور وإيقاف المقدِّم ومعالجة الفشل على حدة، وإن فشل إنشاء الـ work فلا تنتقل إلى التقديم والانتظار والتحرير اللاحقة.
VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PTP_WORK work)
{
// السياق يُثبَّت عند الإنشاء. بيانات كلّ عنصر تُمرَّر عبر طابور متزامن
WORK_QUEUE* queue = (WORK_QUEUE*)context;
ITEM* item = Dequeue(queue); // أخرج عنصراً واحداً تحت الاستبعاد المتبادل
ProcessItem(item);
}
// 1) أنشئ (اربط ردّ النداء بالسياق المشترك، أي الطابور)
PTP_WORK work = CreateThreadpoolWork(WorkCallback, &queue, NULL);
if (!work) { /* معالجة الفشل بـ GetLastError */ }
// 2) قدِّم مرّة لكلّ عنصر تُدرجه (طابق عدد العناصر بعدد التقديمات)
Enqueue(&queue, item);
SubmitThreadpoolWork(work);
// 3) أوقف المقدِّم ثم انتظر الاكتمال (TRUE يحاول أيضاً إلغاء ما لم يبدأ بعد)
WaitForThreadpoolWorkCallbacks(work, FALSE);
// 4) أغلق
CloseThreadpoolWork(work);
flowchart TB
accTitle: دورة حياة كائن work
accDescr: أنشئ بـ CreateThreadpoolWork؛ والتقديم بـ SubmitThreadpoolWork يشغِّل ردود النداء على التوازي. عند الإيقاف، أوقف أوّلاً التقديمات الجديدة، وانتظر اكتمال كلّ ردود النداء بـ WaitForThreadpoolWorkCallbacks، ثمّ أغلق بـ CloseThreadpoolWork
c["أنشئ بـ CreateThreadpoolWork"] --> s["قدِّم بـ SubmitThreadpoolWork (قابل للتكرار)"]
s --> run["تعمل ردود النداء على التوازي"]
run --> stop3["أوقف التقديمات الجديدة"]
stop3 --> w["انتظر الاكتمال بـ WaitForThreadpoolWorkCallbacks"]
w --> cl["أغلق بـ CloseThreadpoolWork"]
الشكل 6: ترتيب الإيقاف «توقّف عن التقديم → انتظر الاكتمال → أغلق». تخطَّ أيّ خطوة فتحصل على استخدام بعد التحرير أو سباق.
4.2 يمكن إعادة استخدام work، لكنّ السياق لا يتغيّر لكلّ تقديم
يمكن تقديم كائن work نفسه عدّة مرّات، حتّى قبل أن ينتهي ردّ النداء السابق. كلّ تقديم يشغِّل ردّ النداء، وتعمل نُسَخ متعدّدة على التوازي. قد يُضبَط عدد مؤشّرات الترابط المستخدم فعليّاً من المجمع للكفاءة.7
ما ينبغي تمييزه هنا الـ work عن بيانات كلّ عنصر عمل. السياق الممرَّر إلى ردّ النداء ثابت وقت الإنشاء. لمعالجة N عنصراً من بيانات مختلفة بـ work واحد، اجعل طابوراً متزامناً السياق، كما في المثال أعلاه. قدِّم مرّة لكلّ عنصر تضعه في الطابور، واجعل ردّ النداء يأخذ عنصراً واحداً من الطابور. تصميم ينشئ كائن work لكلّ عنصر مناسب أيضاً.5
flowchart TB
accTitle: تلقّي بيانات لكلّ عنصر عبر سياق ثابت
accDescr: يُثبَّت السياق على طابور مشترك عند إنشاء الـ work؛ ويقدِّم المقدِّم مرّة لكلّ عنصر يضعه في الطابور، ويأخذ كلّ ردّ نداء يعمل على التوازي عنصراً واحداً في كلّ مرّة من الطابور تحت استبعاد التداخل
create["ثبِّت السياق عند إنشاء الـ work"] -.-> queue["طابور مشترك باستبعاد تداخل"]
item["أدرج عنصراً واحداً"] --> queue
item --> submit["قدِّم مرّة لكلّ عنصر"]
submit --> callbacks["تعمل ردود النداء على التوازي"]
callbacks --> dequeue["أخرج عنصراً واحداً كلّ منها"]
queue --> dequeue
dequeue --> process["عالج العنصر المأخوذ"]
الشكل 7: ما يُثبَّت هو السياق الذي يشير إلى الطابور؛ بيانات كلّ عنصر تُمرَّر عبر الطابور المتزامن.
4.3 عند الإيقاف، توقّف عن التقديم قبل أن تنتظر
ترتيب الإيقاف الآمن «توقّف عن التقديمات الجديدة → انتظر الاكتمال بـ WaitForThreadpoolWorkCallbacks → أغلق بـ CloseThreadpoolWork». الذاكرة التي يشير إليها ردّ النداء يجب ألّا تُحرَّر قبل تأكيد الاكتمال أيضاً. إن حرَّرت ما يُشار إليه بينما تبقى معالجة تعمل أو معلَّقة، تحصل على استخدام بعد التحرير.6
التفكير «استدعيت انتظار الاكتمال، إذن هو آمن» لا يكفي. إن كان مؤشّر ترابط آخر ما يزال يستطيع استدعاء SubmitThreadpoolWork، فتقديم بعد الانتظار يتسابق مع Close. لجعل الانتظار معلماً في الإيقاف، إيقاف المقدِّم أوّلاً مقدّمة.
إن كان المعامل الثاني لـ WaitForThreadpoolWorkCallbacks هو FALSE، ينتظر الاكتمال؛ وإن كان TRUE، يطلب أيضاً إلغاء ردود النداء التي لم تبدأ بعد. ذلك لا يعني أنّ معالجة تعمل أصلاً يجوز تركها. كيفيّة إيقاف عدّة كائنات معاً مشمولة في الفصل 6.
5. انضباط ردّ النداء ── لا تحتكر مؤشّر ترابط مستعاراً ولا تلوِّثه
5.1 أعلن العمل طويل التشغيل، وافحص قيمة الإرجاع أيضاً
يضبط المجمع عدد مؤشّرات الترابط على افتراض أنّ ردود النداء تعود سريعاً. إن واصلت معالجة طويلة أو انتظاراً طويلاً دون إخباره بشيء، يتأخّر تنفيذ ردود نداء أخرى. عندما قد يطول العمل، إمّا أخبر المجمع بتلك الإمكان بـ CallbackMayRunLong أو انقله إلى مؤشّر ترابط مخصَّص.8
غير أنّ استدعاء CallbackMayRunLong وحده لا يكفي. يعيد FALSE عندما لا يستطيع توفير عامل لردود نداء أخرى. بدل تجاهل قيمة الإرجاع ومواصلة الحجب، في تلك الحالة مِل إلى عدم سدّ المجمع: قسِّم المعالجة، انقلها إلى مؤشّر ترابط مخصَّص، وما شابه.8
flowchart TB
accTitle: تقرير كيفيّة معالجة ردّ نداء طويل التشغيل
accDescr: العمل الذي يحتاج معالجة طويلة أو انتظاراً طويلاً إمّا يُنقَل إلى مؤشّر ترابط مخصَّص أو يُعلَن للمجمع بـ CallbackMayRunLong؛ إن عاد FALSE لأنّه لم يمكن توفير عامل آخر، تجنّب الحجب بتقسيم العمل أو نقله إلى مؤشّر ترابط مخصَّص
long["يحتاج معالجة طويلة أو انتظاراً"] --> choice{"أين يشغَّل"}
choice -->|"خصِّص"| dedicated["انقله إلى مؤشّر ترابط مخصَّص"]
choice -->|"شغِّله على المجمع"| notify["أعلن بـ CallbackMayRunLong"]
notify --> result{"هل أُمِّن عامل آخر؟"}
result -->|"TRUE"| run["افعل المعالجة الطويلة"]
result -->|"FALSE"| split["قسِّمه أو انقله إلى مخصَّص"]
الشكل 8: إعلان العمل طويل التشغيل مجموعة تشمل فحص قيمة الإرجاع وتقرير الفعل التالي.
5.2 لا تنتظر عملاً على المجمع نفسه بصورة متزامنة من داخل عامل
تصميم يقدِّم فيه ردّ النداء A العمل B إلى المجمع نفسه وينتظر اكتماله بـ WaitForThreadpoolWorkCallbacks أو ما شابه يحتاج حذراً. متى صار كلّ عامل «ينتظر عمل عامل آخر»، لا يبقى عامل حرّ لتشغيل B، وتحصل على جمود مجاعة مجمع.
العلاج ليس حجز عامل للانتظار بل التغيير إلى شكل استمرار يقدِّم فيه ردّ نداء اكتمال B العمل التالي. لا تكتب الاعتمادات انتظارات تحتلّ عاملاً.
flowchart TB
accTitle: بنية جمود مجاعة مجمع
accDescr: إن انتظر كلّ مؤشّر ترابط عامل اكتمال عمل آخر مقدَّم إلى المجمع نفسه بصورة متزامنة، لا يوجد عامل حرّ يستطيع تشغيل ذلك العمل، وينتظر الجميع إلى الأبد في جمود
w1["العامل 1: ينتظر اكتمال العمل X"] --> q["الأعمال X وY تنتظر التشغيل"]
w2["العامل 2: ينتظر اكتمال العمل Y"] --> q
q -.-> none["لا عامل حرّ لتشغيلهما"]
none -.-> dead["الجميع ينتظر إلى الأبد (جمود مجاعة)"]
الشكل 9: انتظر عاملاً بصورة متزامنة من داخل عامل، ولا يبقى أحد لتشغيل العمل المنتظَر.
5.3 أعد حالة مؤشّر الترابط قبل العودة
يُستخدَم مؤشّر الترابط العامل لردّ النداء التالي غير ذي الصلة أيضاً. ترك الأولويّة متغيّرة، أو ترك حالة تهيئة COM، أو ترك قيمة في TLS، أو نسيان تحرير قفل ينتقل إلى العمل التالي. يجب ألّا تفترض أنّ الدالّة التي تقدِّمها إلى المجمع، أو المعالجة التي تستدعيها، تعمل على مؤشّر ترابط مخصَّص.9
ثمّة أيضاً واجهات تربط التنظيف بنهاية ردّ النداء. مثلاً، يطلب LeaveCriticalSectionWhenCallbackReturns من المجمع تحرير القسم الحرج بعد عودة ردّ النداء.4
flowchart TB
accTitle: لا تنقل حالة مؤشّر الترابط إلى ردّ النداء التالي
accDescr: لأنّ ردّ نداء مختلفاً يعيد استخدام العامل نفسه، العودة وقد تُركت حالة أولويّة أو COM أو TLS أو قفل تلوِّث العمل التالي؛ التنظيف وإعادة الحالة يمنعان ذلك الانتقال
first["ردّ النداء A يستعير عاملاً"] --> cleanup{"نُظِّف قبل العودة؟"}
cleanup -->|"لا"| dirty["أُعيد الاستخدام والحالة متروكة"]
dirty --> impact["يؤثّر على B غير ذي الصلة"]
cleanup -->|"نعم"| clean["أُعيد العامل بحالته الأصليّة"]
clean --> next["يستخدمه ردّ النداء التالي B"]
الشكل 10: مؤشّر الترابط مستعار، لذا أنت مسؤول لا عن نتيجة المعالجة فحسب، بل عن حالة مؤشّر الترابط عندما تعيده.
5.4 لا تدع استثناءات غير معالجة تفلت من العامل
استثناء غير معالَج على مؤشّر ترابط عامل يمكن أن يُسقط العمليّة كلّها. كما مع دالّة مؤشّر ترابط مخصَّص، طبِّق سياسة التقاط الاستثناءات عند نقطة دخول ردّ النداء وتسجيلها. ترك الأمور للمجمع لا يزيل الحاجة إلى معالجة إخفاقات تحدث داخل العمل.
6. تقسيم التكوين ── مجمّعات مخصَّصة ومجموعات تنظيف
6.1 استخدم مجمعاً مخصَّصاً لعزل أنواع العمل
عندما لم يعد المجمع الافتراضي كافياً، يمكنك إنشاء مجمع مستقل بـ CreateThreadpool. يضبط SetThreadpoolThreadMaximum وSetThreadpoolThreadMinimum الحدّين الأعلى والأدنى لعدد مؤشّرات الترابط العاملة.10
الغرض النموذجي العزل. كي لا تستخدم «معالجة دفعة قد تكون بطيئة» عاملي «عمل ينبغي أن يستجيب فوراً»، تقسِّم المجمّعات وتعطي كلّاً ميزانيّة مؤشّرات ترابط. هذا ليس ببساطة إضافة مؤشّرات ترابط؛ إنّه فصل أيّ عمل يستخدم أيّ عاملين.
6.2 اربط هدف التنفيذ والتنظيف معاً ببيئة ردّ نداء
ما يحدِّد أيّ مجمع يُشغَّل عليه هو TP_CALLBACK_ENVIRON. تهيِّئ بيئة ردّ النداء هذه، وتحدِّد المجمع بـ SetThreadpoolCallbackPool، وتمرِّرها إلى CreateThreadpoolWork وما شابه. المعامل الثالث، الذي كان NULL في مثال الفصل 4، هو موضع البيئة.5
عبر البيئة نفسها، يمكن إرفاق مجموعة تنظيف أيضاً. المجمع المخصَّص هدف التنفيذ؛ مجموعة التنظيف وحدة التنظيف. رؤية هذين الدورين منفصلين يجعل التكوين أسهل تتبّعاً.
flowchart TB
accTitle: ربط التكوين عبر بيئة ردّ نداء
accDescr: تشير بيئة ردّ النداء إلى مجمع مخصَّص ومجموعة تنظيف؛ وكائنات work وtimer المُنشأة بتلك البيئة تعمل على ذلك المجمع، وعملية مجموعة التنظيف بالجملة تجمع انتظار الاكتمال والتحرير
env["بيئة ردّ النداء (TP_CALLBACK_ENVIRON)"] --> cp["مجمع مخصَّص (يتحكّم في عدد مؤشّرات الترابط)"]
env --> cg["مجموعة تنظيف"]
env --> obj["تُمرَّر عند إنشاء work / timer / wait / io"]
cg -.-> close["انتظر الاكتمال وحرِّر بالجملة"]
الشكل 11: بيئة ردّ النداء آليّة تحقن «على أيّ مجمع يعمل ومن ينظِّف» عند إنشاء الكائن.
6.3 اجمع انتظار الاكتمال والتحرير لعدّة كائنات
كلّما تكاثرت كائنات work وtimer وغيرها داخل وحدة، يصير الإيقاف قائمة «انتظر كلّاً، أغلق كلّاً». إن أنشأت مجموعة بـ CreateThreadpoolCleanupGroup وجعلت الكائنات التي تنشئها عبر بيئة ردّ النداء أعضاء فيها، يجمع CloseThreadpoolCleanupGroupMembers واحد انتظار الاكتمال والتحرير لكلّ كائنات الأعضاء.46
هنا أيضاً، الهدف ألّا تترك ردّ نداء يعمل. اختر بين إيقاف كائنات work فرديّاً، كما في الفصل 4، والإيقاف على مستوى المجموعة، وفق عدد الكائنات التي تديرها.
7. استخدامه من DLL ── لا تُلغِ التحميل قبل ردود النداء
7.1 انتظر الاكتمال في دالّة إيقاف صريحة، لا في DllMain
أخطر شيء في DLL أن يُلغى تحميل الـDLL بينما شيفرة ردّ ندائه ما تزال تستطيع التنفيذ. إن عملت شيفرة أُلغي تحميلها، تحصل على انتهاك وصول.
القاعدة الأساسيّة توقّف عن التقديم، وانتظر اكتمال ردود النداء، وأغلق الكائنات في دالّة إيقاف الـDLL الصريحة قبل إلغاء التحميل. سواء بدوال انتظار فرديّة أو بمجموعة التنظيف من الفصل 6، يجب ألّا يُتخطَّى فحص الاكتمال هذا.6
لا تؤدِّ هذا الانتظار داخل DllMain. بسبب علاقته بقفل المحمِّل، يسبّب جموداً مختلفاً. انظر مقالة DllMain وقفل المحمِّل للتفاصيل.
7.2 اقرن FreeLibraryWhenCallbackReturns بمرجع أُخذ قبل التقديم
للحالة «هذا ردّ النداء العمل الأخير، لذا أريد تحرير مرجع الـDLL بعد عودته»، ثمّة FreeLibraryWhenCallbackReturns.4
غير أنّ هذه الواجهة وحدها لا تمنع إلغاء تحميل قبل بدء ردّ النداء. ما تفعله تحرير مرجع وحدة واحد عندما يعود ردّ النداء العامل. استخدمها زوجاً: خذ مرجع وحدة لتلك المعالجة بـ GetModuleHandleEx قبل التقديم، وحرِّره من ردّ النداء بهذه الواجهة.
flowchart TB
accTitle: إغلاق الـDLL في دالّة إيقاف مقابل إعادة مرجع من ردّ النداء
accDescr: الشكل الأساسي دالّة إيقاف غير DllMain توقّف التقديمات وتنتظر الاكتمال وتحرِّر قبل إلغاء تحميل الـDLL؛ في تصميم يعيد فيه ردّ النداء الأخير المرجع، خذ المرجع بـ GetModuleHandleEx قبل التقديم واقرنه بالتحرير بعد العودة عبر FreeLibraryWhenCallbackReturns
shutdown["دالّة إيقاف صريحة"] --> stop["توقّف عن التقديم، انتظر، حرِّر"]
stop --> unload["ثمّ ألغِ تحميل الـDLL"]
note["لا تنتظر في DllMain"] -.-> shutdown
acquire["خذ مرجع وحدة قبل التقديم"] --> submit["قدِّم ردّ النداء"]
submit --> callback["جدول التحرير داخل ردّ النداء"]
api["FreeLibraryWhenCallbackReturns"] -.-> callback
callback --> returned["حرِّر مرجعاً واحداً بعد العودة"]
الشكل 12: لا تخلط انتظار الاكتمال في دالّة الإيقاف بتحرير المرجع المأخوذ لردّ النداء؛ في الحالتين، صمِّم عمر الـDLL أوّلاً.
8. الخلاصة ── ابدأ بـ work، واستبدل مع الإيقاف معاً
مجمع مؤشّرات الترابط Win32 الأساس الذي يغيِّر التزامن في الشيفرة الأصليّة من «إنشاء مؤشّرات ترابط» إلى «تسليم أعمال ردود نداء». يمكنك تجميع الأعمال قصيرة العمر ومؤشّرات الترابط التي لا تفعل سوى الانتظار وترك إدارة مؤشّرات الترابط لنظام التشغيل.
يمكن أن يكون التبنّي تدريجيّاً. أوّلاً، مع work، اجعل الإنشاء والتقديم وإيقاف التقديمات وانتظار الاكتمال والتحرير مجموعة واحدة. ثمّ استبدل مؤشّرات الترابط التي لا تفعل سوى الانتظار بـ wait، ومؤشّرات الترابط التي لا تفعل سوى المؤقِّت بـ timer. انظر في تكامل io وتقسيم المجمع عندما يصيران لازمين.
flowchart TB
accTitle: ترحيل الشيفرة القائمة إلى مجمع مؤشّرات الترابط على مراحل
accDescr: راجع مؤشّرات الترابط ذاتيّة الإدارة القائمة، وأبقِ العمل الذي يناسب المكتبة القياسيّة أو مؤشّر ترابط مخصَّصاً، وانقل العمل المناسب للمجمع إلى كائنات work شاملاً إيقاف التقديمات وانتظار الاكتمال، واستبدل الانتظارات بـ wait والمؤقِّتات بـ timer على مراحل
inventory["راجع مؤشّرات الترابط ذاتيّة الإدارة القائمة"] --> choose{"يناسب المجمع؟"}
choose -->|"لا"| keep["اختر المكتبة القياسيّة أو مخصَّصاً"]
choose -->|"نعم"| work["انقل إلى work مع الإيقاف مجموعة"]
work --> more["الانتظارات إلى wait، والمؤقِّتات إلى timer"]
more --> optional["تكامل الإدخال/الإخراج، تقسيم المجمع حسب الحاجة"]
الشكل 13: لا تقليل مؤشّرات الترابط فحسب بل إكمال الإيقاف في كلّ مرحلة أساس ترحيل مرحلي.
ما تبقيه حتّى النهاية ثلاثة انضباطات: لا تحجب طويلاً، ولا تنتظر بصورة متزامنة على المجمع نفسه، ولا تلوِّث حالة مؤشّر الترابط. في DLL، امنع إضافيّاً سباقات مع إلغاء التحميل. اترك ما تستطيع أدوات C++ القياسيّة أو .NET معالجته لها، واستخدم هذه الواجهة حيث يلزم تكامل أو تحكّم خاصّ بـ Windows.
مقالات ذات صلة
- أعماق إدخال/إخراج Windows (الجزء 3) ── منافذ اكتمال الإدخال/الإخراج (IOCP) ومجمع مؤشّرات الترابط في .NET: القبو تحت async/await
- أفضل الممارسات العمليّة لتعدّد مؤشّرات الترابط: نسخة C++
- أفضل ممارسات تعدّد مؤشّرات الترابط عمليّاً: طبعة C
- الإيقاظات الزائفة ── لماذا تستيقظ متغيّرات الشرط «دون إشعار» وكيف تنتظر بشكل صحيح على Windows
- DllMain وقفل المحمِّل ── السبب الحقيقي لعبارة «لا تفعل شيئاً في تهيئة الـDLL»
- لماذا ينبغي تفضيل انتظار الأحداث على Sleep(1) في Windows
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع تصميم الترحيل من شيفرة أصليّة تكاثرت مؤشّرات ترابطها إلى مجمع مؤشّرات الترابط، ومراجعات تصميم التزامن في تطبيقات C++ وDLLs، وتحقيق السبب الجذري لتعليق وانهيار يسبّبهما مجاعة مجمع أو ردود نداء. نرحّب بالاستشارة بدءاً من جرد شيفرتك القائمة.
روابط مرجعيّة
-
Microsoft Learn, Thread Pools. حول كون مجمع مؤشّرات الترابط مجموعة مؤشّرات ترابط عاملة تنفِّذ بكفاءة ردود نداء غير متزامنة نيابة عن التطبيق؛ وأنواع التطبيق التي يناسبها (إصدار عدد كبير من عناصر عمل صغيرة على التوازي، وإنشاء وإتلاف مؤشّرات ترابط قصيرة العمر بكثرة، ومعالجة عمل مستقل على التوازي، وانتظارات حصريّة على كائنات نواة، وما شابه)؛ وإعادة التصميم الكاملة في Vista (توحيد أنواع مؤشّر الترابط العامل، وطابور مؤقِّت واحد، ومؤشّرات ترابط دائمة مخصَّصة، ومجموعات تنظيف، وعدّة مجمّعات داخل عمليّة، والواجهة الجديدة). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, <future>. حول التنفيذ غير المتزامن على مستوى المهمّة عبر std::async وfuture الذي توفِّره المكتبة القياسيّة، بحيث يمكن كتابة التزامن دون إدارة مؤشّرات الترابط مباشرة. ↩
-
Microsoft Learn, Thread Pooling. حول بنية واجهة مجمع مؤشّرات الترابط التراث (QueueUserWorkItem، وطوابير المؤقِّت، والانتظارات المسجَّلة، وBindIoCompletionCallback)؛ وعدم وجود طريقة لإلغاء عمل بعد وضعه في الطابور؛ وتصريحها الصريح بأنّ واجهة مجمع مؤشّرات الترابط الجديدة المقدَّمة في Vista أبسط وأفضل في الموثوقيّة والأداء والمرونة. ↩ ↩2
-
Microsoft Learn, threadpoolapiset.h header. حول قائمة الدوال التي تشمل دوال إنشاء الكائنات الأربعة CreateThreadpoolWork وCreateThreadpoolTimer وCreateThreadpoolWait وCreateThreadpoolIo؛ ومجموعات التنظيف (CreateThreadpoolCleanupGroup)؛ والتنظيف المربوط باكتمال ردّ النداء (LeaveCriticalSectionWhenCallbackReturns وFreeLibraryWhenCallbackReturns وما شابه). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CreateThreadpoolWork function (threadpoolapiset.h). حول إنشاء كائن work من دالّة ردّ نداء ومؤشّر سياق؛ والمعامل الثالث، TP_CALLBACK_ENVIRON، الذي يستطيع تحديد بيئة تنفيذ ردّ النداء (المجمع الذي ينتمي إليه، وما شابه)، مع كون NULL يعني أنّه يعمل في البيئة الافتراضيّة. ↩ ↩2 ↩3
-
Microsoft Learn, Using the Thread Pool Functions. حول الإجراء الأساسي للإنشاء بـ CreateThreadpoolWork، والتقديم بـ SubmitThreadpoolWork، وانتظار الاكتمال بـ WaitForThreadpoolWorkCallbacks، والإغلاق بـ CloseThreadpoolWork؛ ومثال تكوين يجمع مجمعاً مخصَّصاً مع بيئة ردّ نداء ومجموعة تنظيف. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, SubmitThreadpoolWork function (threadpoolapiset.h). حول إمكان تقديم كائن work نفسه عدّة مرّات دون انتظار اكتمال ردّ نداء سابق، فتعمل ردود النداء على التوازي؛ وقدرة المجمع على ضبط (خنق) عدد مؤشّرات الترابط للكفاءة. ↩
-
Microsoft Learn, CallbackMayRunLong function (threadpoolapiset.h). حول إشعار المجمع بأنّ ردّ النداء الحالي قد يعمل طويلاً، وهو ما يستخدمه المجمع لتقرير هل يؤمِّن مؤشّرات ترابط لردود نداء أخرى؛ والنظر في مؤشّر ترابط مخصَّص لردّ نداء طويل التشغيل حيث أمكن. ↩ ↩2
-
Microsoft Learn, Thread Pooling. حول وجوب أن تكون عناصر العمل المقدَّمة إلى مجمع مؤشّرات الترابط، والدوال التي تستدعيها، آمنة للمجمع؛ وعدم افتراض أنّ مؤشّر الترابط المنفِّذ مؤشّر ترابط دائم مخصَّص؛ وتجنّب استخدام TLS والاستدعاءات غير المتزامنة التي تحتاج مؤشّر ترابط دائماً. ↩
-
Microsoft Learn, SetThreadpoolThreadMaximum function (threadpoolapiset.h). حول إمكان ضبط حدّ أعلى لعدد مؤشّرات الترابط العاملة لمجمع أُنشئ بـ CreateThreadpool (الحدّ الأدنى هو SetThreadpoolThreadMinimum). ↩
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
DllMain وقفل المحمِّل ── السبب الحقيقي لعبارة «لا تفعل شيئاً في تهيئة الـDLL»
لماذا يُحظَر استدعاء LoadLibrary أو مزامنة مؤشّرات الترابط من DllMain. نشرح من المصادر الأوّليّة كيف يسلسل قفل المحمِّل كلّ إشعار DLL، وس...
لماذا تتعطّل الوسائط ── قواعد وسائط سطر أوامر Windows
في Windows لا توجد مصفوفة وسائط؛ يصل إلى CreateProcess سلسلة واحدة ويتولّى الطرف المستقبل تقسيمها. نشرح قواعد CommandLineToArgvW وCRT و.N...
ما الذي يبقى بعد موت الأب ── تربية العمليات الابنة في كائن Job
لماذا يبقى مساعد SDK ماسكاً الكاميرا أو منفذ COM بعد إنهاء الواجهة قسراً. كيف تجعل كائن Job شجرة العمليات وحدة واحدة وتصمم عمر العملية ال...
الأنابيب المسمّاة عمليّاً ── IPC القياسيّ في Windows من التصميم إلى الأمان
دليل عمليّ للأنابيب المسمّاة، وسيلة IPC القياسيّة في Windows. يغطّي وضع البايت مقابل وضع الرسالة، وخوادم متعدّدة العملاء، وأمان ACL وانتح...
ما الذي يعنيه «لا يستجيب» حقّاً ── كيف يقرِّر Windows أنّ تطبيقاً تجمّد، وكيف تصمِّم تطبيقات لا تتجمّد
يحكم Windows أنّ نافذة «لا تستجيب» بعد 5 ثوانٍ بلا استخراج رسالة ويعرض نافذة شبح: الحكم، وأسباب التعليق، وتصميم مؤشّر ترابط الواجهة، وإجر...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- مقارنة بإنشاء مؤشّرات ترابط خاصّة بي بـ CreateThread، ما الأفضل في مجمع مؤشّرات الترابط؟
- الكفاءة عندما تعالج عدداً كبيراً من الأعمال قصيرة العمر، وقلّة شيفرة إدارة مؤشّرات الترابط. لإنشاء مؤشّر ترابط وإتلافه كلفة لا يمكن تجاهلها، فتطبيق يكرِّر «CreateThread لكلّ عمل وإتلافه عند الانتهاء»، أو يمسك كثيراً من مؤشّرات الترابط التي تنام فقط لانتظار حدث، يستطيع خفض عدد مؤشّرات الترابط وتبديلات السياق بالانتقال إلى مجمع. تسرد الوثائق الرسميّة أيضاً مرشّحين للمجمع تطبيقات تُصدر عدداً كبيراً من عناصر العمل الصغيرة على التوازي، وتطبيقات تنشئ كثيراً من مؤشّرات الترابط قصيرة العمر، وتطبيقات تملك مؤشّرات ترابط مخصَّصة لانتظار كائنات النواة. بالمقابل، عمل يحتاج فيه «مؤشّر الترابط نفسه شخصيّة»، كتغيير أولويّة أو COM STA أو معالجة مخصَّصة تظلّ تعمل طويلاً، ينبغي أن يبقى على مؤشّر ترابط مخصَّص كما كان.
- كيف يختلف عن دوال مجمع مؤشّرات الترابط الأقدم مثل QueueUserWorkItem؟
- أُعيد تصميم مجمع مؤشّرات الترابط بالكامل في Windows Vista. عائلة threadpoolapiset الحاليّة (CreateThreadpoolWork وما شابه) الواجهة الجديدة؛ وQueueUserWorkItem وRegisterWaitForSingleObject وما شابه الواجهة القديمة (التراث). توحِّد الواجهة الجديدة أنواع مؤشّر الترابط العامل، وتتيح إنشاء عدّة مجمّعات مستقلّة داخل عمليّة، وتوفِّر آليّات كالتحرير بالجملة عبر مجموعة تنظيف وتحرير قفل أو إلغاء تحميل DLL مربوط باكتمال ردّ النداء. تنصّ الوثائق الرسميّة أيضاً على أنّ الواجهة الجديدة أبسط وأفضل في الموثوقيّة والأداء والمرونة. للواجهة التراث قيود بنيويّة أيضاً، مثل «لا توجد طريقة لإلغاء عمل بعد وضعه في الطابور»، لذا استخدم الواجهة الجديدة في الشيفرة الجديدة.
- هل ثمّة أشياء يجب ألّا أفعلها داخل ردّ نداء؟
- ثلاثة رئيسة. الأولى، الحجب طويلاً أو تشغيل معالجة طويلة عند الافتراضيّات. يضبط المجمع عدد مؤشّرات الترابط على افتراض أنّ ردود النداء تنتهي سريعاً، لذا لمعالجة تطول إمّا أعلن عنها بـ CallbackMayRunLong أو استخدم مؤشّر ترابط مخصَّصاً. الثانية، انتظار اكتمال عمل آخر مقدَّم إلى المجمع نفسه بصورة متزامنة. عندما ينتهي كلّ عامل إلى «انتظار عامل آخر»، تحصل على جمود مجاعة مجمع. الثالثة، الاعتماد على شخصيّة مؤشّر الترابط. مؤشّرات الترابط العاملة مشتركة بين ردود النداء، لذا العودة وقد تغيّرت أولويّة مؤشّر الترابط أو حالة تهيئة COM، أو ترك حالة في TLS، يلوِّث ردّ النداء التالي. للتنظيف في النهاية (تحرير قفل أو إلغاء تحميل DLL)، تُوفَّر آليّات مخصَّصة مثل LeaveCriticalSectionWhenCallbackReturns وFreeLibraryWhenCallbackReturns.
- هل ثمّة ما يُحتاط له عند استخدام مجمع مؤشّرات الترابط داخل DLL؟
- الخطر الأكبر «يُلغى تحميل الـDLL بينما ردّ نداء ما يزال يعمل». إن نفَّذ ردّ نداء بعد إلغاء التحميل، تحصل على انتهاك وصول. في معالجة إيقافه، يجب أن ينتظر الـDLL بموثوقيّة اكتمال ردود النداء التي أصدرها، باستخدام دالّة انتظار مثل WaitForThreadpoolWorkCallbacks (أو CloseThreadpoolCleanupGroupMembers على مجموعة تنظيف)، قبل إغلاق الكائنات. غير أنّ فعل هذا الانتظار داخل DllMain يمكن أن يجمد عبر تفاعله مع قفل المحمِّل، لذا القاعدة فعله في دالّة إيقاف صريحة، لا في DllMain. للحالة التي يكون فيها ردّ النداء نفسه «العمل الأخير» ويريد تحرير الـDLL، تُوفَّر واجهة مخصَّصة، FreeLibraryWhenCallbackReturns.
- الآن وقد صار في C++ std::async وفي .NET ThreadPool، هل ما تزال ثمّة مناسبات لاستخدام هذه الواجهة مباشرة؟
- نعم. المعيار «هل تكفي أداة تلك الطبقة». إن غطّت حبيبات التزامن التي تحتاجها std::async أو std::thread في C++، فالمكتبة القياسيّة المرشّح الأوّل، من منظور قابليّة النقل أيضاً. من جهة أخرى، الرغبة في توحيد مؤقِّتات وانتظارات كائنات نواة واكتمال إدخال/إخراج غير متزامن في آليّة ردّ نداء واحدة، أو الرغبة في تقسيم المجمّعات والتحكّم في عدد مؤشّرات الترابط لكلّ نوع عمل، أو عدم الرغبة في ملكيّة مؤشّرات ترابط داخل DLL أو مكوِّن COM، متطلّبات يغطّيها مجمع مؤشّرات الترابط Win32. العلاقة مع ThreadPool في .NET وIOCP مشمولة في مقالة ذات صلة، وما دمت تكتب شيفرة أصليّة، معرفة هذه الآليّة في الطبقة تحتها ليست هدراً أبداً.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.