عندما يتحدثون عن تنفيذ البرنامج ، فإن "التنفيذ غير المتزامن" يعني موقفًا لا ينتظر فيه البرنامج إتمام عملية معينة ، لكنه يواصل العمل بشكل مستقل عنه. مثال على البرمجة غير المتزامنة هو أداة مساعدة ، تعمل بشكل غير متزامن ، تكتب إلى ملف سجل. على الرغم من أن هذه الأداة قد تفشل (على سبيل المثال ، بسبب نقص مساحة القرص الحرة) ، إلا أنها ستعمل في معظم الحالات بشكل صحيح ويمكن استخدامها في برامج مختلفة. سيكونون قادرين على الاتصال بها ، ويمررونها البيانات للتسجيل ، وبعد ذلك يمكنهم الاستمرار في عمل ما يفعلونه.

استخدام آليات غير متزامنة عند كتابة برنامج معين يعني أن هذا البرنامج سوف يعمل بشكل أسرع من دون استخدام هذه الآليات. في الوقت نفسه ، يجب كتابة ما تم التخطيط لإطلاقه بشكل غير متزامن ، مثل أداة مساعدة للتسجيل ، مع مراعاة حالات الطوارئ. على سبيل المثال ، الأداة المساعدة للتسجيل ، إذا نفدت مساحة القرص ، يمكن ببساطة إيقاف التسجيل ، وليس "تعطل" البرنامج الرئيسي مع وجود خطأ.
يتضمن تنفيذ التعليمات البرمجية غير المتزامنة عادة تشغيل هذه التعليمات البرمجية في مؤشر ترابط منفصل. هذا - إذا كنا نتحدث عن نظام مع معالج أحادي النواة. في الأنظمة التي تحتوي على معالجات متعددة النواة ، قد يتم تنفيذ هذا الرمز من خلال عملية تستخدم نواة منفصلة. يمكن للمعالج أحادي النواة في وقت معين قراءة وتنفيذ تعليمة واحدة فقط. هو مثل قراءة الكتب. لا يمكنك قراءة كتابين في نفس الوقت.
إذا كنت تقرأ كتابًا ويقدم لك شخصًا آخر كتابًا آخر ، فيمكنك أخذ هذا الكتاب الثاني والبدء في قراءته. لكن الأول يجب أن يؤجل. يتم تنفيذ التعليمات البرمجية متعددة الخيوط على نفس المبدأ. وإذا كانت العديد من نسخك ستقرأ عدة كتب مرة واحدة ، فسيكون مشابهًا لكيفية عمل أنظمة المعالجات المتعددة.
إذا كان التبديل بين المهام التي تتطلب طاقة حوسبة مختلفة (على سبيل المثال ، بين عمليات حسابية معينة وقراءة البيانات من قرص) على معالج أحادي النواة ، فقد تشعر أن نواة معالج واحدة تقوم بعدة أشياء في نفس الوقت. أو ، على سبيل المثال ، يحدث هذا إذا حاولت فتح عدة مواقع في مستعرض في وقت واحد. إذا كان المتصفح يستخدم دفقًا منفصلًا لتحميل كل صفحة ، فسيتم إجراء كل شيء بشكل أسرع من تحميل هذه الصفحات صفحة واحدة في كل مرة. لا يعد تحميل الصفحة مهمة صعبة ، فهو لا يستخدم موارد النظام إلى الحد الأقصى ، ونتيجة لذلك ، يعد الإطلاق المتزامن للعديد من هذه المهام خطوة فعالة للغاية.
برمجة بايثون غير المتزامن
في البداية ، استخدمت بيثون كوروتينات قائمة على المولد لحل مهام البرمجة غير المتزامنة. بعد ذلك ، في Python 3.4 ،
asyncio
الوحدة غير
asyncio
(في بعض الأحيان يتم كتابة اسمها باسم
async IO
) ، والتي تنفذ آليات البرمجة غير المتزامنة. قدمت بايثون 3.5 متزامن / تنتظر البناء.
من أجل القيام بتطوير غير متزامن في بيثون ، تحتاج إلى التعامل مع اثنين من المفاهيم. هذه هي coroutine والمهمة.
Korutiny
عادة ، coroutine هي وظيفة غير متزامن. يمكن أن يكون Coroutine أيضًا كائنًا يتم إرجاعه من وظيفة coroutine.
عند الإشارة إلى أنها غير متزامنة ، عند الإعلان عن وظيفة ، يمكنك تسميتها باستخدام الكلمة الرئيسية "
await
:
await say_after(1, 'hello')
مثل هذا الإنشاء يعني أن البرنامج سيتم تنفيذه حتى يواجه تعبير انتظار ، وبعد ذلك سوف يستدعي الوظيفة ويوقف تنفيذه مؤقتًا حتى يتم الانتهاء من عمل الوظيفة المطلوبة. بعد ذلك ، سيتمكّن كوريوتينيون آخرون من البدء.
إيقاف برنامج يعني أن التحكم يعود إلى حلقة الحدث. عند استخدام الوحدة النمطية غير المتزامنة ، تنفذ حلقة الأحداث جميع المهام غير المتزامنة ، وتقوم بإجراء I / O ، وتنفذ العمليات الفرعية. في معظم الحالات ، يتم استخدام المهام لتشغيل corutin.
المهام
تتيح لك المهام تشغيل coroutines في حلقة حدث. هذا يبسط السيطرة على تنفيذ coroutines عدة. هنا مثال يستخدم coroutines والمهام. لاحظ أن الكيانات التي تم إعلانها باستخدام
async def
build هي coroutines. هذا المثال مأخوذ من
الوثائق الرسمية لبيثون.
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}")
تحتوي وظيفة
say_after()
على بادئة غير
say_after()
؛ ونتيجة لذلك ، لدينا coroutine. إذا ابتعدنا قليلاً عن هذا المثال ، فيمكننا القول أن هذه الوظيفة يمكن أن يطلق عليها مثل هذا:
await say_after(1, 'hello') await say_after(2, 'world')
مع هذا النهج ، ومع ذلك ، يتم استدعاء coroutines بالتتابع ويستغرق حوالي 3 ثوان لإكمال. في مثالنا ، يتم إطلاقها بشكل تنافسي. لكل منهم يتم استخدام المهمة. نتيجة لذلك ، فإن وقت تنفيذ البرنامج بأكمله حوالي 2 ثانية. يرجى ملاحظة أنه لكي يعمل مثل هذا البرنامج ، لا يكفي مجرد الإعلان عن الوظيفة
main()
باستخدام
async
. في مثل هذه الحالات ، تحتاج إلى استخدام الوحدة غير
asyncio
.
إذا قمت بتشغيل رمز المثال ، فسيتم عرض نص مشابه للنص التالي على الشاشة:
started at 20:19:39 hello world finished at 20:19:41
لاحظ أن الطوابع الزمنية في الأسطر الأول والأخير تختلف بمقدار ثانيتين. إذا قمت بتشغيل هذا المثال باستدعاء متتابع من corutin ، فسيكون الفرق بين الطوابع الزمنية 3 ثوانٍ بالفعل.
مثال
في هذا المثال ، يتم تحديد عدد العمليات المطلوبة لحساب مجموع عشرة عناصر من سلسلة من الأرقام. يتم إجراء الحسابات بدءًا من نهاية التسلسل. تبدأ الدالة العودية بالحصول على الرقم 10 ، ثم تتصل بنفسها بالأرقام 9 و 8 ، وتضيف ما سيتم إرجاعه. يستمر هذا حتى يتم الانتهاء من العمليات الحسابية. نتيجة لذلك ، اتضح ، على سبيل المثال ، أن مجموع سلسلة من الأرقام من 1 إلى 10 هو 55. في الوقت نفسه ،
time.sleep(0.1)
غير فعالة للغاية ، يتم استخدام بناء
time.sleep(0.1)
هنا.
هنا هو رمز الوظيفة:
import time def fib(n): global count count=count+1 time.sleep(0.1) if n > 1: return fib(n-1) + fib(n-2) return n start=time.time() global count count = 0 result = fib(10) print(result,count) print(time.time()-start)
ماذا يحدث إذا قمت بإعادة كتابة هذا الرمز باستخدام آليات غير متزامنة وقمت بتطبيق بناء
asyncio.gather
، وهو المسؤول عن أداء مهمتين وانتظار
asyncio.gather
؟
import asyncio,time async def fib(n): global count count=count+1 time.sleep(0.1) event_loop = asyncio.get_event_loop() if n > 1: task1 = asyncio.create_task(fib(n-1)) task2 = asyncio.create_task(fib(n-2)) await asyncio.gather(task1,task2) return task1.result()+task2.result() return n
في الواقع ، يعمل هذا المثال أبطأ قليلاً من المثال السابق ، حيث يتم تنفيذ كل شيء في مؤشر ترابط واحد ، ويدعو إلى
create_task
،
gather
وغيرها مثل ذلك إنشاء تحميل إضافي على النظام. ومع ذلك ، فإن الغرض من هذا المثال هو إظهار القدرة على التنافس في مهام متعددة وانتظار استكمالها.
النتائج
هناك حالات يكون فيها استخدام المهام و corutin مفيدًا جدًا ، على سبيل المثال ، إذا كان البرنامج يحتوي على مزيج من المدخلات والحسابات ، أو في حالة إجراء حسابات مختلفة في نفس البرنامج ، يمكنك حل هذه المشكلات عن طريق تشغيل الكود بطريقة تنافسية بدلاً من في الوضع المتسلسل. يساعد هذا في تقليل الوقت اللازم للبرنامج لتنفيذ إجراءات معينة. ومع ذلك ، هذا لا يسمح ، على سبيل المثال ، بإجراء العمليات الحسابية في وقت واحد. يتم استخدام المعالجة المتعددة لتنظيم مثل هذه الحسابات. هذا هو موضوع كبير منفصل.
أعزائي القراء! كيف تكتب كود بايثون غير متزامن؟
