وحدة `concurrent` في بايثون
في هذا المقال، سنشرح وحدة concurrent في بايثون۔
مع توضيح مفهومي التزامن والمعالجة المتوازية، سنشرح كيفية تنفيذ المعالجة غير المتزامنة باستخدام وحدة concurrent مع أمثلة عملية۔
YouTube Video
وحدة concurrent في بايثون
عند تسريع المعالجة في بايثون، من المهم الانتباه للفروق بين التزامن والمعالجة المتوازية۔ وحدة concurrent هي وسيلة هامة لإجراء المعالجة غير المتزامنة بشكل آمن وبسيط مع مراعاة هذه الفروقات۔
الفرق بين التزامن والمعالجة المتوازية
-
التزامن يعني تصميم العملية بحيث يتم تنفيذ عدة مهام عن طريق التبديل بينها في وحدات عمل صغيرة۔ حتى لو لم تكن المهام تعمل فعلياً في نفس الوقت، فإن استخدام "وقت الانتظار" يجعل العملية بأكملها أكثر كفاءة۔
-
المعالجة المتوازية تعني تشغيل عدة مهام فيزيائياً في نفس الوقت۔ باستخدام عدة نوى للمعالج، يتم تسريع التنفيذ بشكل متزامن۔
كلاهما تقنيتان لتسريع المعالجة، لكن التزامن يتعلق بكيفية تنظيم العمليات، والمعالجة المتوازية تتعلق بكيفية تنفيذها فعلياً، مما يجعلهما مختلفتين جذرياً۔
ما هي وحدة concurrent؟
concurrent هي مكتبة قياسية في بايثون توفر واجهة برمجة تطبيقات عالية المستوى للتعامل مع التزامن والمعالجة المتوازية بشكل آمن وبسيط۔ تم تصميمها بحيث يمكنك التركيز على 'تنفيذ المهام' دون القلق بشأن العمليات منخفضة المستوى مثل إنشاء وإدارة الخيوط أو العمليات۔
أدوار ThreadPoolExecutor وProcessPoolExecutor
توفر وحدة concurrent خيارين رئيسيين حسب نوع المهمة۔
-
ThreadPoolExecutorهذا مناسب للتنفيذ المتزامن، خاصة المهام التي تحتوي على العديد من أوقات الانتظار لإدخال/إخراج، مثل عمليات الشبكة أو الملفات۔ من خلال التبديل بين المهام، يتم استخدام وقت الانتظار بشكل فعال۔ -
ProcessPoolExecutorهذا التنفيذ موجه للمعالجة المتوازية ومناسب للمهام التي تستهلك قدرًا كبيرًا من وحدة المعالجة المركزية۔ يستخدم عدة عمليات للاستفادة الكاملة من نوى المعالج المتوفرة بشكل متوازي۔
وبالتالي، من الميزات الرئيسية لوحدة concurrent أنها توفر بنية تتيح لك الاختيار المناسب بين التزامن والمعالجة المتوازية حسب الحاجة۔
أساسيات ThreadPoolExecutor (لمهام الإدخال/الإخراج)
ThreadPoolExecutor مناسب للمهام التي تعتمد على الإدخال/الإخراج، مثل الاتصال بالشبكة وعمليات الملفات۔ يتم توزيع المهام على عدة خيوط، مما يتيح استخدامًا فعالًا لوقت الانتظار۔
1from concurrent.futures import ThreadPoolExecutor
2import time
3
4def fetch_data(n):
5 # Simulate an I/O-bound task
6 time.sleep(1)
7 return f"data-{n}"
8
9with ThreadPoolExecutor(max_workers=3) as executor:
10 futures = [executor.submit(fetch_data, i) for i in range(5)]
11
12 for future in futures:
13 print(future.result())- في هذا المثال، يتم تنفيذ عدة مهام إدخال/إخراج تنتظر لمدة ثانية واحدة بشكل متزامن۔ باستخدام
submit، يتم تسجيل استدعاءات الدوال كمهام غير متزامنة، وباستخدامresult()، يمكنك الانتظار حتى تكتمل والحصول على النتائج، مما يتيح تنفيذ معالجة متزامنة تستغل وقت الانتظار بشكل مختصر وفعال۔
تزامن بسيط باستخدام map
إذا لم تكن هناك حاجة لتحكم معقد، فإن استخدام map يمكن أن يجعل الكود أكثر اختصاراً۔
1from concurrent.futures import ThreadPoolExecutor
2import time
3
4def fetch_data(n):
5 # Simulate an I/O-bound task
6 time.sleep(1)
7 return f"data-{n}"
8
9with ThreadPoolExecutor(max_workers=3) as executor:
10 results = executor.map(fetch_data, range(5))
11
12 for result in results:
13 print(result)- في هذا المثال، يتم تنفيذ عدة مهام إدخال/إخراج بشكل متزامن باستخدام
ThreadPoolExecutor.map۔ بما أنmapيعيد النتائج بنفس ترتيب المدخلات، يمكنك كتابة كود قريب من التنفيذ التسلسلي، والتنفيذ المتزامن ممكن دون الحاجة للانتباه لمعالجة عدم التزامن—وهذا ميزة كبيرة۔
أساسيات ProcessPoolExecutor (لمهام المعالجة المكثفة)
في الحسابات الثقيلة التي تستخدم المعالج بالكامل، عليك استخدام العمليات بدلاً من الخيوط۔ هذا يسمح بتجنب قيود قفل المترجم العالمي (GIL)۔
1from concurrent.futures import ProcessPoolExecutor
2
3def heavy_calculation(n):
4 # Simulate a CPU-bound task
5 total = 0
6 for i in range(10_000_000):
7 total += i * n
8 return total
9
10if __name__ == "__main__":
11 with ProcessPoolExecutor(max_workers=4) as executor:
12 results = executor.map(heavy_calculation, range(4))
13
14 for result in results:
15 print(result)في هذا المثال، يتم تنفيذ عمليات معالجة مكثفة في المعالج بشكل متوازٍ باستخدام ProcessPoolExecutor۔ بما أن هناك حاجة لإنشاء عمليات منفصلة، يجب وجود حاجز __main__، مما يسمح بتنفيذ آمن لمعالجة متوازية باستخدام عدة أنوية للمعالج۔
المعالجة حسب ترتيب الاكتمال باستخدام as_completed
as_completed مفيد عندما تريد التعامل مع النتائج حسب ترتيب اكتمالها۔
1from concurrent.futures import ThreadPoolExecutor, as_completed
2import time
3
4def fetch_data(n):
5 # Simulate an I/O-bound task
6 time.sleep(1)
7 return f"data-{n}"
8
9with ThreadPoolExecutor(max_workers=3) as executor:
10 futures = [executor.submit(fetch_data, i) for i in range(5)]
11
12 for future in as_completed(futures):
13 print(future.result())- في هذا المثال، يتم تنفيذ عدة مهام غير متزامنة في نفس الوقت، ويتم استرجاع النتائج حسب ترتيب اكتمالها۔ استخدام
as_completedيتيح لك التعامل مع النتائج بسرعة بغض النظر عن ترتيب المهام، مما يجعله مناسباً لعرض التقدم أو الحالات التي تتطلب معالجة متسلسلة۔
التعامل مع الاستثناءات
في concurrent، تظهر الاستثناءات عند استدعاء result()۔
1from concurrent.futures import ThreadPoolExecutor
2
3def risky_task(n):
4 # Simulate a task that may fail for a specific input
5 if n == 3:
6 raise ValueError("Something went wrong")
7 return n * 2
8
9with ThreadPoolExecutor() as executor:
10 futures = [executor.submit(risky_task, i) for i in range(5)]
11
12 for future in futures:
13 try:
14 print(future.result())
15 except Exception as e:
16 print("Error:", e)- يوضح هذا المثال أنه حتى إذا ظهرت استثناءات في بعض المهام، تواصل باقي المهام التنفيذ ويمكنك معالجة الاستثناءات بشكل فردي عند استخراج النتائج۔ من خلال استخدام
Futureفيconcurrent، من المهم أن تتمكن من التعامل مع النجاح والإخفاق في عمليات المعالجة غير المتزامنة بأمان۔
إرشادات لاختيار بين الخيوط والعمليات
لاستخدام التزامن والمعالجة المتوازية بشكل فعال، من المهم اختيار المنهجية المناسبة حسب نوع المهمة۔
عملياً، المعايير التالية يمكن أن تساعدك في اتخاذ القرار۔
- بالنسبة للعمليات التي تحتوي على الكثير من أوقات انتظار الإدخال/الإخراج، مثل الاتصال أو عمليات الملفات، استخدم
ThreadPoolExecutor۔ - لمهام المعالجة المكثفة على المعالج، استخدم
ProcessPoolExecutor۔ - إذا كانت هناك مهام بسيطة عديدة، فاستخدام
mapيسمح لك بكتابة كود مختصر أكثر۔ - إذا كان التحكم الدقيق في ترتيب التنفيذ أو التعامل مع الاستثناءات مهماً، اجمع بين
submitوas_completed۔
فوائد استخدام concurrent
باستخدام وحدة concurrent، يمكنك تنفيذ المعالجة غير المتزامنة بشكل آمن وبديهي۔
الفوائد الرئيسية كما يلي:۔
- لن تحتاج للانشغال بإدارة الخيوط أو العمليات على المستوى المنخفض۔
- يتم توفيرها كجزء من مكتبة بايثون القياسية، لذا يمكنك استخدامها بثقة۔
- يصبح الكود أكثر قابلية للقراءة والصيانة۔
- إنها مثالية كخطوة أولى في تعلم التزامن والمعالجة المتوازية۔
فقط تذكر هذه الإرشادات يمكن أن يقلل بشكل كبير من الفشل في التطبيقات التي تستخدم concurrent۔
الملخص
وحدة concurrent هي الخيار القياسي للتزامن والمعالجة المتوازية العملية في بايثون۔ تتيح لك تعزيز الأداء دون تغيير كبير في محتوى عملياتك البرمجية، وهو ما يعد ميزة كبيرة في التطبيق العملي۔ باستخدام concurrent، يمكنك تنفيذ المعالجة غير المتزامنة بشكل مختصر مع الإدارة الآمنة للتعامل مع الاستثناءات والتحكم في التنفيذ۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔