تعدد الخيوط في Python مع مثال: تعلم GIL في Python
⚡ ملخص ذكي
تعدد الخيوط في Python يُشغّل هذا النظام عدة خيوط داخل عملية واحدة، مما يسمح لها بمشاركة الذاكرة والعمل بالتزامن. تقوم وحدة الخيوط بإنشاء هذه الخيوط وإدارتها، بينما يحد قفل المفسر العام من التوازي الحقيقي، مما يجعل هذه التقنية الأنسب للمهام المرتبطة بالإدخال والإخراج.

استخدم Python تتيح لك لغات البرمجة استخدام المعالجة المتعددة أو الخيوط المتعددة. في هذا الدرس، ستتعلم كيفية كتابة تطبيقات متعددة الخيوط في Python.
ما هو الموضوع؟
الخيط هو وحدة تنفيذ في البرمجة المتزامنة. تعدد الخيوط هو أسلوب يسمح لوحدة المعالجة المركزية بتنفيذ العديد من مهام عملية واحدة في الوقت نفسه. يمكن لهذه الخيوط أن تعمل بشكل فردي مع مشاركة موارد العملية.
ما هي العملية؟
العملية هي في الأساس البرنامج قيد التنفيذ. عندما تبدأ تشغيل تطبيق على جهاز الكمبيوتر الخاص بك (مثل متصفح أو محرر نصوص)، يقوم نظام التشغيل بإنشاء .
ما هو تعدد في Python?
تعدد الخيوط في Python البرمجة هي تقنية معروفة تتشارك فيها خيوط متعددة ضمن عملية واحدة مساحة البيانات مع الخيط الرئيسي، مما يُسهّل تبادل المعلومات والتواصل بين الخيوط بكفاءة عالية. الخيوط أخف وزنًا من العمليات، حيث يمكن تنفيذ عدة خيوط بشكل منفصل مع مشاركة موارد العملية. يهدف تعدد الخيوط إلى تشغيل مهام ووظائف متعددة في الوقت نفسه.
ما هي المعالجة المتعددة؟
المعالجة المتعددة يتيح لك تشغيل عمليات متعددة غير مرتبطة في نفس الوقت. لا تتشارك هذه العمليات مواردها وتتواصل من خلال IPC.
Python تعدد العمليات مقابل المعالجة المتعددة
لفهم العمليات والخيوط، تخيل هذا السيناريو: ملف .exe على جهاز الكمبيوتر الخاص بك هو برنامج. عند فتحه، يقوم نظام التشغيل بتحميله في الذاكرة، ثم يقوم المعالج بتنفيذه. تُسمى نسخة البرنامج التي تعمل الآن بالعملية.
لكل عملية مكونان أساسيان:
- استخدم Code
- البيانات
الآن، يمكن أن تحتوي العملية على جزء فرعي واحد أو أكثر يسمى الخيوط. يعتمد هذا على بنية نظام التشغيل. يمكنك اعتبار الخيط بمثابة جزء من العملية يمكن لنظام التشغيل تنفيذه بشكل منفصل.
بمعنى آخر، هي سلسلة من التعليمات التي يمكن لنظام التشغيل تشغيلها بشكل مستقل. تتشارك الخيوط داخل عملية واحدة بيانات تلك العملية، وهي مصممة للعمل معًا لتسهيل التوازي.
لماذا استخدام تعدد المواضيع؟
يتيح لك تعدد المهام تقسيم التطبيق إلى مهام فرعية متعددة وتشغيل هذه المهام في وقت واحد. إذا استخدمت تعدد المهام بشكل صحيح، فيمكن تحسين سرعة التطبيق والأداء والعرض.
Python خاصية تعدد
Python يدعم هذا البرنامج بنىً لكل من المعالجة المتعددة والخيوط المتعددة. في هذا البرنامج التعليمي، ستركز بشكل أساسي على التنفيذ. مؤشرات التطبيقات ذات Pythonهناك وحدتان رئيسيتان يمكن استخدامهما للتعامل مع الخيوط في Python:
- استخدم خيط الوحدة النمطية، و
- استخدم خيوط وحدة
ومع ذلك، في Pythonيوجد أيضًا ما يُسمى بقفل المفسر العام (GIL). وهو لا يُتيح تحسينًا كبيرًا في الأداء، بل قد يُؤدي إلى نتائج عكسية. تخفيض أداء بعض التطبيقات متعددة الخيوط. سوف تتعلم كل شيء عنها في الأقسام القادمة من هذا البرنامج التعليمي.
وحدات الخيط والخيوط
الوحدتان اللتان ستتعرف عليهما في هذا البرنامج التعليمي هما وحدة الموضوع و مبادئ السلوك وحدة خيوط.
ومع ذلك، فقد تم إهمال وحدة مؤشر الترابط منذ فترة طويلة. بدءا من Python 3، تم تصنيفه على أنه قديم ولا يمكن الوصول إليه إلا كـ _خيط للتوافق مع الإصدارات السابقة.
يجب عليك استخدام المستوى الأعلى خيوط وحدة للتطبيقات التي تنوي نشرها. تم تناول وحدة الخيوط هنا لأغراض تعليمية فقط.
وحدة الموضوع
بناء الجملة لإنشاء موضوع جديد باستخدام هذه الوحدة هو كما يلي:
thread.start_new_thread(function_name, arguments)
حسنًا، لقد قمت الآن بتغطية النظرية الأساسية لبدء البرمجة. لذلك، افتح الخاص بك IDLE أو مفكرة واكتب فيها ما يلي:
import time import _thread def thread_test(name, wait): i = 0 while i <= 3: time.sleep(wait) print("Running %s\n" %name) i = i + 1 print("%s has finished execution" %name) if __name__ == "__main__": _thread.start_new_thread(thread_test, ("First Thread", 1)) _thread.start_new_thread(thread_test, ("Second Thread", 2)) _thread.start_new_thread(thread_test, ("Third Thread", 3))
احفظ الملف واضغط على F5 لتشغيل البرنامج. إذا تم كل شيء بشكل صحيح، فهذه هي النتيجة التي يجب أن تراها:
ستتعرف على المزيد حول ظروف السباق وكيفية التعامل معها في الأقسام القادمة.
شرح الكود
- تستورد هذه العبارات وحدة الوقت ووحدة الخيوط، واللتان تُستخدمان للتعامل مع تنفيذ وتأخير... Python الخيوط.
- هنا قمت بتعريف دالة تسمى موضوع_اختبار, والذي سوف يطلق عليه start_new_thread تقوم هذه الدالة بتشغيل حلقة تكرارية (while loop) لأربع دورات، ثم تطبع اسم الخيط الذي استدعاها. عند اكتمال الدورة، تطبع رسالة تفيد بأن الخيط قد أنهى تنفيذه.
- هذا هو القسم الرئيسي لبرنامجك. هنا، يمكنك ببساطة الاتصال بـ start_new_thread الطريقة مع thread_test دالة كوسيط. سيؤدي هذا إلى إنشاء سلسلة عمليات جديدة للدالة التي تمررها كوسيط وبدء تنفيذها. لاحظ أنه يمكنك استبدال (thread_test) بأي دالة أخرى تريد تشغيلها كسلسلة عمليات.
وحدة الخيوط
هذه الوحدة هي التنفيذ عالي المستوى للترابط في Python وهو المعيار الفعلي لإدارة التطبيقات متعددة الخيوط. يوفر مجموعة واسعة من الميزات مقارنةً بوحدة الخيوط.
هيكل وحدة الخيوط
فيما يلي قائمة ببعض الوظائف المفيدة المحددة في هذه الوحدة:
| اسم الوظيفة | الوصف |
|---|---|
| العدد النشط () | إرجاع عدد خيط الأشياء التي لا تزال حية. |
| الموضوع الحالي () | إرجاع الكائن الحالي لفئة الموضوع. |
| تعداد () | يسرد كافة كائنات الموضوع النشطة. |
| isDaemon() | يُرجع صحيحًا إذا كان الخيط خفيًا. |
| حي() | يُرجع صحيحًا إذا كان الخيط لا يزال حيًا. |
| طرق فئة الموضوع | |
| بداية() | يبدأ نشاط الموضوع. يجب أن يتم استدعاؤه مرة واحدة فقط لكل مؤشر ترابط لأنه سيؤدي إلى حدوث خطأ في وقت التشغيل إذا تم استدعاؤه عدة مرات. |
| يركض() | تشير هذه الطريقة إلى نشاط مؤشر الترابط ويمكن تجاوزها بواسطة فئة تعمل على توسيع فئة الموضوع. |
| انضم() | إنه يمنع تنفيذ تعليمات برمجية أخرى حتى يتم إنهاء الخيط الذي تم استدعاء طريقة join () عليه. |
الخلفية الدرامية: فئة الموضوع
قبل البدء في كتابة برامج متعددة الخيوط باستخدام وحدة الخيوط، من الضروري فهم فئة Thread. فئة Thread هي الفئة الأساسية التي تحدد قالب وعمليات الخيط في Python.
الطريقة الأكثر شيوعًا لإنشاء متعدد الخيوط Python يتمثل التطبيق في تعريف فئة ترث من فئة Thread وتتجاوز طريقة run() الخاصة بها.
تشير فئة Thread، باختصار، إلى تسلسل تعليمات برمجية يتم تشغيله بشكل منفصل خيط السيطرة.
لذا، عند كتابة تطبيق متعدد الخيوط، ستقوم بما يلي:
- قم بتعريف فئة ترث من فئة Thread
- تجاوز __init__ منشئ
- تجاوز يركض() طريقة
بمجرد إنشاء كائن مؤشر ترابط، بداية() يمكن استخدام هذه الطريقة لبدء تنفيذ هذا النشاط، و انضم() يمكن استخدام الطريقة لحظر كافة التعليمات البرمجية الأخرى حتى انتهاء النشاط الحالي.
والآن، دعونا نحاول استخدام وحدة الخيوط لتنفيذ المثال السابق. مرة أخرى، قم بتشغيل IDLE واكتب ما يلي:
import time import threading class threadtester (threading.Thread): def __init__(self, id, name, i): threading.Thread.__init__(self) self.id = id self.name = name self.i = i def run(self): thread_test(self.name, self.i, 5) print ("%s has finished execution " %self.name) def thread_test(name, wait, i): while i: time.sleep(wait) print ("Running %s \n" %name) i = i - 1 if __name__=="__main__": thread1 = threadtester(1, "First Thread", 1) thread2 = threadtester(2, "Second Thread", 2) thread3 = threadtester(3, "Third Thread", 3) thread1.start() thread2.start() thread3.start() thread1.join() thread2.join() thread3.join()
سيكون هذا هو الإخراج عند تنفيذ التعليمات البرمجية أعلاه:
شرح الكود
- هذا الجزء مماثل للمثال السابق. هنا، تقوم باستيراد وحدة الوقت ووحدة الخيوط، واللتان تُستخدمان للتعامل مع تنفيذ وتأخيرات... Python الخيوط.
- في هذا الجزء، تقوم بإنشاء فئة تسمى Threadtester، والتي ترث أو توسع نطاق خيط فئة من وحدة الخيوط. هذه إحدى أكثر الطرق شيوعًا لإنشاء الخيوط في Pythonومع ذلك، يجب عليك فقط تجاوز الدالة البانية و يركض() الطريقة في تطبيقك كما ترون في نموذج التعليمات البرمجية أعلاه، فإن __init__ تم تجاوز الطريقة (المنشئ). وبالمثل، لقد تجاوزت أيضًا يركض() طريقة. يحتوي على الكود الذي تريد تنفيذه داخل سلسلة رسائل. في هذا المثال، قمت باستدعاء الدالة thread_test().
- هذه هي دالة thread_test()، التي تأخذ قيمة i كوسيطة، يقوم بإنقاصها بمقدار 1 في كل تكرار، ويكرر بقية الكود حتى تصبح i 0. في كل تكرار، يقوم بطباعة اسم الخيط قيد التنفيذ حاليًا وينام لمدة wait ثانية (والتي يتم أخذها أيضًا كوسيط).
- thread1 = threadtester(1, “First Thread”، 1) هنا، نقوم بإنشاء موضوع وتمرير المعلمات الثلاثة التي أعلنا عنها في __init__. المعلمة الأولى هي معرف الخيط، والمعلمة الثانية هي اسم الخيط، والمعلمة الثالثة هي العداد، الذي يحدد عدد المرات التي يجب أن يتم فيها تشغيل الحلقة.
- تُستخدم الدالة `start()` لبدء تنفيذ سلسلة العمليات. داخليًا، تستدعي الدالة `start()` الدالة `run()` الخاصة بالفئة.
- thread3.join() تمنع طريقة join() تنفيذ تعليمات برمجية أخرى وتنتظر حتى ينتهي الخيط الذي تم استدعاؤه عليه.
كما تعلمون، فإن الخيوط الموجودة في نفس العملية لديها إمكانية الوصول إلى ذاكرة وبيانات تلك العملية. ونتيجة لذلك، إذا حاول أكثر من خيط واحد تغيير البيانات أو الوصول إليها في الوقت نفسه، فقد تحدث أخطاء.
في القسم التالي، سترى أنواعًا مختلفة من التعقيدات التي يمكن أن تظهر عندما تصل الخيوط إلى البيانات والقسم الحرج دون التحقق من معاملات الوصول الحالية.
الجمود وظروف السباق
قبل التعرف على حالات الجمود وحالات التزامن، سيكون من المفيد فهم بعض التعريفات الأساسية المتعلقة بالبرمجة المتزامنة:
- جزء حرج: هو جزء من التعليمات البرمجية التي تصل إلى المتغيرات المشتركة أو تعدّلها، ويجب تنفيذها كمعاملة ذرية.
- تبديل السياق: إنها العملية التي يتبعها المعالج لتخزين حالة الخيط قبل الانتقال من مهمة إلى أخرى بحيث يمكن استئنافها من نفس النقطة لاحقًا.
جمود
جمود تُعدّ هذه المشكلة من أكثر المشاكل التي يخشاها المطورون عند كتابة تطبيقات متزامنة/متعددة الخيوط في Pythonأفضل طريقة لفهم حالات الجمود هي استخدام مثال مشكلة علوم الحاسوب الكلاسيكية المعروفة باسم السفره Philoمشكلة الصوفيين.
المشكلة التي يطرحها فلاسفة الطعام هي كما يلي:
يجلس خمسة فلاسفة على طاولة مستديرة مع خمسة أطباق من السباغيتي (نوع من المعكرونة) وخمس شوكات، كما هو موضح في الرسم التخطيطي.
السفره Philoمشكلة الصوفيين
في أي وقت من الأوقات، يجب على الفيلسوف أن يأكل أو يفكر.
علاوة على ذلك، يجب على الفيلسوف أن يأخذ الشوكتين المجاورتين له (أي الشوكة اليسرى والشوكة اليمنى) قبل أن يتمكن من تناول السباغيتي. تحدث مشكلة الجمود عندما يأخذ الفلاسفة الخمسة شوكاتهم اليمنى في نفس الوقت.
بما أن كل فيلسوف لديه شوكة واحدة، فسوف ينتظرون جميعًا أن يضع الآخرون شوكاتهم، ونتيجة لذلك، لن يتمكن أي منهم من تناول السباغيتي.
على نحو مماثل، في النظام المتزامن، يحدث الجمود عندما تحاول خيوط أو عمليات مختلفة (فلاسفة) الحصول على موارد النظام المشتركة (الشوك) في نفس الوقت. ونتيجة لهذا، لا تحصل أي من العمليات على فرصة للتنفيذ لأنها تنتظر موردًا آخر تحتفظ به عملية أخرى.
شروط السباق
حالة التزامن هي حالة غير مرغوب فيها في البرنامج تحدث عندما يقوم النظام بتنفيذ عمليتين أو أكثر في وقت واحد. على سبيل المثال، انظر إلى حلقة التكرار البسيطة التالية:
i=0; # a global variable for x in range(100): print(i) i+=1;
إذا قمت بإنشاء ملفات n بغض النظر عن عدد الخيوط التي تُشغّل هذا الكود في وقت واحد، لا يمكنك تحديد قيمة i (المشتركة بين الخيوط) عند انتهاء تنفيذ البرنامج. والسبب في ذلك هو أنه في بيئة تعدد الخيوط الحقيقية، قد تتداخل الخيوط، وقد تتغير قيمة i التي استرجعها أحد الخيوط وعدّلها بين وصول خيط آخر إليها.
هذان هما النوعان الرئيسيان من المشاكل التي يمكن أن تحدث في بيئة متعددة الخيوط أو موزعة Python التطبيق. في القسم التالي، ستتعلم كيفية التغلب على هذه المشكلة عن طريق مزامنة الخيوط.
Syncكرونة المواضيع
للتعامل مع ظروف السباق والجمود والمشكلات الأخرى القائمة على الخيوط، توفر وحدة الخيوط قفل الفكرة هي أنه عندما يريد أحد الخيوط الوصول إلى مورد معين، فإنه يحصل على قفل لهذا المورد. بمجرد قيام أحد الخيوط بقفل مورد معين، لا يمكن لأي خيط آخر الوصول إليه حتى يتم تحرير القفل. ونتيجة لذلك، ستكون التغييرات على المورد ذرية، وسيتم تجنب ظروف السباق.
القفل هو بدائية مزامنة منخفضة المستوى يتم تنفيذها بواسطة _خيط الوحدة. في أي وقت، يمكن أن يكون القفل في إحدى حالتين: مقفل or مفتوحة. وهو يدعم طريقتين:
- يكتسب(): عند فتح حالة القفل، سيؤدي استدعاء الدالة acquire() إلى تغيير الحالة إلى مقفلة والعودة. أما إذا كانت الحالة مقفلة، فسيتم حظر استدعاء الدالة acquire() حتى يتم استدعاء الدالة release() بواسطة خيط آخر.
- يطلق(): يتم استخدام طريقة الإصدار () لتعيين الحالة على إلغاء القفل، أي لتحرير القفل. يمكن استدعاؤه بواسطة أي خيط، وليس بالضرورة الخيط الذي حصل على القفل.
إليك مثال على استخدام الأقفال في تطبيقاتك. شغّل تطبيقك IDLE واكتب ما يلي:
import threading lock = threading.Lock() def first_function(): for i in range(5): lock.acquire() print ('lock acquired') print ('Executing the first funcion') lock.release() def second_function(): for i in range(5): lock.acquire() print ('lock acquired') print ('Executing the second funcion') lock.release() if __name__=="__main__": thread_one = threading.Thread(target=first_function) thread_two = threading.Thread(target=second_function) thread_one.start() thread_two.start() thread_one.join() thread_two.join()
الآن، اضغط على F5. يجب أن تشاهد إخراجًا مثل هذا:
شرح الكود
- هنا، أنت ببساطة تقوم بإنشاء قفل جديد عن طريق الاتصال بـ خيوط. قفل () وظيفة المصنع . داخليًا، تقوم Lock() بإرجاع مثيل لفئة القفل الملموسة الأكثر فعالية والتي يتم الحفاظ عليها بواسطة النظام الأساسي.
- في العبارة الأولى، يمكنك الحصول على القفل عن طريق استدعاء طريقة الاستحواذ (). عندما يتم منح القفل، يمكنك الطباعة "تم الحصول على القفل" إلى وحدة التحكم. بمجرد الانتهاء من تنفيذ جميع التعليمات البرمجية التي تريد تشغيل مؤشر الترابط، يمكنك تحرير القفل عن طريق استدعاء طريقة الإصدار ().
النظرية جيدة، ولكن كيف تتأكد من أن القفل قد نجح فعلاً؟ إذا نظرت إلى المخرجات، ستلاحظ أن كل عبارة طباعة تطبع سطرًا واحدًا فقط في كل مرة. تذكر أنه في مثال سابق، كانت مخرجات الطباعة عشوائية لأن عدة سلاسل عمليات كانت تستخدم دالة الطباعة في الوقت نفسه. هنا، لا تُستدعى دالة الطباعة إلا بعد الحصول على القفل. لذا، تُعرض المخرجات سطرًا سطرًا.
بصرف النظر عن الأقفال، Python كما يدعم بعض الآليات الأخرى للتعامل مع مزامنة الخيوط، كما هو موضح أدناه:
- رلوكس
- Semaphores
- الشروط
- الأحداث و
- حواجز
قفل المترجم العالمي (وكيفية التعامل معه)
قبل الدخول في تفاصيل Pythonيا طلاب GIL، دعونا نعرّف بعض المصطلحات التي ستكون مفيدة في فهم القسم القادم:
- التعليمات البرمجية المرتبطة بوحدة المعالجة المركزية: يشير هذا إلى أي جزء من التعليمات البرمجية التي سيتم تنفيذها مباشرة بواسطة وحدة المعالجة المركزية.
- التعليمات البرمجية المرتبطة بالإدخال/الإخراج: يمكن أن تكون هذه أي تعليمات برمجية تصل إلى نظام الملفات من خلال نظام التشغيل.
- CPython: هو المرجع التنفيذ of Python ويمكن وصفه بأنه مترجم مكتوب بلغة C و Python (لغة البرمجة).
ما هو جيل في Python?
قفل المترجم العالمي (GIL) in Python قفل العملية (GIL) هو قفل أو مُضاعِف يُستخدم عند التعامل مع العمليات. يضمن هذا القفل وصول خيط واحد فقط إلى مورد مُحدد في كل مرة، كما يمنع استخدام الكائنات والرموز الثنائية في آنٍ واحد. يُفيد هذا البرامج أحادية الخيوط بزيادة في الأداء. Python الأمر بسيط للغاية وسهل التنفيذ.
يمكن استخدام القفل للتأكد من أن مؤشر ترابط واحد فقط لديه حق الوصول إلى مورد معين في وقت معين.
إحدى ميزات Python يتمثل ذلك في استخدامه لقفل عام على كل عملية مترجم، مما يعني أن كل عملية تعامل Python تفسير نفسه كمورد.
على سبيل المثال، لنفترض أنك كتبت Python برنامج يستخدم خيطين لتنفيذ عمليات المعالجة المركزية وعمليات الإدخال/الإخراج. عند تشغيل هذا البرنامج، يحدث ما يلي:
- استخدم Python يقوم المترجم بإنشاء عملية جديدة وينشئ الخيوط.
- عندما يبدأ تشغيل مؤشر الترابط 1، سيحصل أولاً على GIL ويقفله.
- إذا أراد مؤشر الترابط 2 التنفيذ الآن، فسيتعين عليه الانتظار حتى يتم إصدار GIL حتى لو كان هناك معالج آخر مجاني.
- الآن، لنفترض أن الخيط 1 ينتظر عملية إدخال/إخراج. في هذا الوقت، سيحرر GIL، وسيقوم الخيط 2 بالحصول عليه.
- بعد إكمال عمليات الإدخال/الإخراج، إذا أراد مؤشر الترابط 1 التنفيذ الآن، فسيتعين عليه الانتظار مرة أخرى حتى يتم إصدار GIL بواسطة مؤشر الترابط 2.
ونتيجة لذلك، لا يمكن إلا لخيط واحد الوصول إلى المفسر في أي وقت، مما يعني أنه لن يكون هناك سوى خيط واحد قيد التنفيذ. Python الكود في لحظة زمنية معينة.
يُعدّ هذا الأمر مقبولاً في المعالجات أحادية النواة، لأنها تستخدم تقنية تقسيم الوقت (انظر القسم الأول من هذا الدليل) لإدارة الخيوط. أما في حالة المعالجات متعددة النوى، فإنّ تنفيذ دالة مُرهِقة لوحدة المعالجة المركزية على خيوط متعددة سيؤثر بشكل كبير على كفاءة البرنامج، إذ لن تستخدم جميع النوى المتاحة في الوقت نفسه.
لماذا كانت هناك حاجة لجيل؟
وCPython يستخدم جامع البيانات المهملة تقنية فعالة لإدارة الذاكرة تُعرف باسم عد المراجع. إليك كيفية عملها: كل كائن في Python يحتوي الكائن على عداد مرجعي، يزداد عند إسناده إلى اسم متغير جديد أو إضافته إلى حاوية (مثل الصفوف والقوائم، إلخ). وبالمثل، ينخفض العداد المرجعي عند خروج المرجع من نطاق التعريف أو عند استدعاء عبارة الحذف. عندما يصل العداد المرجعي للكائن إلى الصفر، يتم جمعه بواسطة جامع البيانات المهملة، ويتم تحرير الذاكرة المخصصة له.
لكن المشكلة تكمن في أن متغير عدد المراجع عرضة لحالات التزامن مثل أي متغير عام آخر. ولحل هذه المشكلة، قام مطورو Python قررنا استخدام قفل المفسر العام. الخيار الآخر كان إضافة قفل لكل كائن، الأمر الذي كان سيؤدي إلى حالات جمود وزيادة في الحمل الزائد الناتج عن استدعاءات acquire() و release().
لذلك، يُعد قفل المفسر العام (GIL) قيدًا هامًا للمعالجة متعددة الخيوط Python البرامج التي تُشغّل عمليات مُرهقة لوحدة المعالجة المركزية (مما يجعلها فعليًا أحادية الخيوط). إذا كنت ترغب في استخدام أنوية معالجة مركزية متعددة في تطبيقك، فاستخدم المعالجة المتعددة وحدة بدلا من ذلك.








