"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > البرمجة غير المتزامنة مع Asyncio

البرمجة غير المتزامنة مع Asyncio

تم النشر بتاريخ 2024-08-21
تصفح:947

Asynchronous Programming with Asyncio

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

البرمجة المتزامنة

عندما تعمل عدة كيانات مستقلة في وقت واحد، تكون البرمجة متزامنة. وهذا لا يعني بالضرورة أن هذه المهام تعمل في نفس الوقت بالضبط. وبدلاً من ذلك، فهذا يعني أن المهام تحرز تقدمًا بمرور الوقت من خلال مشاركة الموارد، مثل وقت وحدة المعالجة المركزية (CPU). الميزة الرئيسية للبرمجة المتزامنة هي متانتها: إذا تعطلت إحدى العمليات، يستمر باقي البرنامج في العمل.

البرمجة الموازية

إذا تمكنت الخوارزمية من تقسيم عملها إلى عدة أجزاء، فهي متوازية. كلما زاد عدد المعالجات لديك، زادت استفادتك من التوازي. تعمل البرمجة المتوازية الفعالة على تحسين موارد الأجهزة الحديثة للحصول على أداء أفضل.

توضيح التزامن مقابل التوازي مع الطبخ

مثال على التزامن:

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

مثال على التوازي:

الآن، لنفترض أن لديك صديقًا لمساعدتك. بينما تركز أنت على شواء اللحم، يعتني صديقك بتحضير الصلصة. يتم تنفيذ كلتا المهمتين في وقت واحد دون الحاجة إلى تبديل الانتباه بينهما. وهذا يمثل التوازي.

ما هي البرمجة غير المتزامنة؟

تتضمن البرمجة غير المتزامنة التعامل مع عمليات الإدخال/الإخراج (I/O) التي تحدث خارج برنامجك، مثل إدخال المستخدم، أو الطباعة إلى محطة طرفية، أو القراءة من مأخذ توصيل، أو الكتابة إلى القرص. الخصائص الرئيسية للإدخال/الإخراج غير المتزامن هي:

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

  • لا يستطيع البرنامج التنبؤ بموعد انتهاء العملية.

بالنسبة للخدمات التي تحتوي على عمليات إدخال/إخراج كبيرة (مثل خوادم الويب وقواعد البيانات والبرامج النصية للنشر)، فإن تحسين هذه العمليات يمكن أن يؤدي إلى تحسين الأداء بشكل كبير.

دعونا نرى أمثلة على كود الحظر والكود غير المحظور.

مثال على رمز الحظر وعدم الحظر

فكر في برنامج بسيط:

import time

def task():
    time.sleep(2)
    print("Hello")

for _ in range(3):
    task()

في هذا البرنامج المتزامن، تنتظر كل مهمة حتى تنتهي المهمة السابقة، مما يسبب التأخير.

الآن، دعونا نلقي نظرة على إصدار غير متزامن باستخدام غير المتزامن:

import asyncio

async def task():
    await asyncio.sleep(2)
    print("Hello")

async def main():
    tasks = [task() for _ in range(3)]
    await asyncio.gather(*tasks)

asyncio.run(main())

في هذا البرنامج غير المتزامن، يتم تشغيل المهام بشكل متزامن، مما يقلل إجمالي وقت التنفيذ. دعونا نستكشف مكونات البرمجة غير المتزامنة.

مكونات البرمجة غير المتزامنة

حلقات الأحداث، والكوروتينات، والعقود الآجلة هي العناصر الأساسية لبرنامج بايثون غير المتزامن.

  • حلقة الأحداث: تدير تبديل المهام وتدفق التنفيذ، وتتبع المهام ليتم تشغيلها بشكل غير متزامن.

  • Coroutines: وظائف خاصة يمكن إيقافها مؤقتًا واستئنافها، مما يسمح بتشغيل المهام الأخرى أثناء الانتظار. يحدد الكوروتين المكان الذي يجب أن يحدث فيه حدث تبديل المهام في الوظيفة، مما يعيد التحكم إلى حلقة الحدث. عادةً ما يتم إنشاء Coroutines بواسطة حلقة الحدث ويتم تخزينها داخليًا في قائمة انتظار المهام.

  • العقود الآجلة: العناصر النائبة للنتائج من coroutines، وتخزين النتيجة أو الاستثناءات. بمجرد أن تبدأ حلقة الحدث كوروتين، يتم إنشاء مستقبل مناظر يخزن نتيجة الكوروتين، أو استثناء إذا تم طرحه أثناء تنفيذ الكوروتين.

مع شرح الأجزاء المهمة من البرمجة غير المتزامنة في بايثون، دعونا نكتب بعض التعليمات البرمجية.

كتابة التعليمات البرمجية غير المتزامنة

الآن بعد أن فهمت نمط البرمجة غير المتزامن، فلنكتب نصًا صغيرًا ونحلل التنفيذ. إليك نصًا بسيطًا غير متزامن:

import asyncio

async def task():
    await asyncio.sleep(2)
    print("Hello")

async def main():
    tasks = [task() for _ in range(3)]
    await asyncio.gather(*tasks)

asyncio.run(main())

في الكود أعلاه، نحاول مواصلة تنفيذ المهام الأخرى حتى لو كان هناك تنفيذ آخر في وضع السكون (الحظر). لاحظ الكلمة الأساسية غير المتزامنة أمام المهمة والوظائف الرئيسية.

هذه الوظائف أصبحت الآن coroutines.

تُسبق وظائف Coroutines في Python بالكلمة الأساسية غير المتزامنة. الوظيفة الرئيسية () هنا هي منسق المهام أو حلقة الحدث الفردية لدينا، حيث أنها تنفذ جميع المهام باستخدام طريقة async.gather. تقوم الدالة asyncio.gather بتشغيل الكائنات المنتظرة بشكل متزامن.

الإخراج:

Hello
Hello
Hello
Program executed in 2.01 seconds.

عندما تصل كل مهمة إلى انتظار asyncio.sleep(2)، فإنها ببساطة تنتقل إلى المهمة التالية وتعود عند الانتهاء. إنه مثل القول: "سوف أنام لمدة ثانيتين. افعل شيئًا آخر."

دعونا نرى النسخة المتزامنة لإجراء مقارنة سريعة.

import time

def task():
    time.sleep(2)
    print("Hello")

for _ in range(3):
    task()

في الكود أعلاه، سنتبع طريقة البرمجة التقليدية في بايثون. ستلاحظ أن تنفيذ العملية سيستغرق وقتًا أطول بكثير.

الإخراج:

Hello
Hello
Hello
Program executed in 6.01 seconds.

الآن يمكنك ملاحظة وقت التنفيذ. فكر في time.sleep()‎ كمهمة محظورة وasyncio.sleep()‎ كمهمة غير محظورة أو طويلة. في البرمجة غير المتزامنة، تتمثل فائدة انتظار شيء ما، مثل asyncio.sleep()، في أن الوظيفة المحيطة يمكن أن تتنازل مؤقتًا عن التحكم لوظيفة أخرى جاهزة للتنفيذ على الفور.

مع فهم بعض الأمثلة الأساسية للبرمجة غير المتزامنة في بايثون، دعنا نستكشف قواعد البرمجة غير المتزامنة في بايثون.

قواعد البرمجة Asyncio

  1. Coroutines: لا يمكن تنفيذ Coroutines مباشرة. إذا حاولت تشغيل دالة coroutine مباشرةً، فستُرجع كائن coroutine. بدلاً من ذلك، استخدم asyncio.run():

    import asyncio
    
    async def hello():
        await asyncio.sleep(1)
        print('Hello')
    
    asyncio.run(hello())
    
  2. الكائنات المنتظرة: Coroutines والمستقبلات والمهام هي الكائنات الرئيسية المنتظرة. كوروتينات بايثون قابلة للانتظار ويمكن انتظارها من كوروتينات أخرى.

  3. انتظار الكلمة الرئيسية:لا يمكن استخدام الانتظار إلا ضمن وظائف غير متزامنة.

    async def hello():
        await asyncio.sleep(1)
        print("Hello")
    
  4. التوافق: ليست كل وحدات بايثون متوافقة مع البرمجة غير المتزامنة. على سبيل المثال، سيؤدي استبدال التابع wait asyncio.sleep()‎ بالتابع time.sleep()‎ إلى حدوث خطأ. يمكنك التحقق من قائمة الوحدات المتوافقة والتي تمت صيانتها هنا.

في القسم التالي، سنستكشف الاستخدام الشائع للبرمجة غير المتزامنة، وطلبات HTTP.

مثال البرنامج: الطلبات غير المتزامنة

دعونا نلقي نظرة على الجزء التالي من الكود:

import aiohttp
import asyncio

async def fetch(session, city):
    url = f"https://www.prevision-meteo.ch/services/json/{city}"
    async with session.get(url) as response:
        data = await response.json()
        print(f"Temperature at {city}: {data['current_condition']['tmp']} C")

async def main():
    async with aiohttp.ClientSession() as session:
        cities = ['paris', 'toulouse', 'marseille']
        tasks = [fetch(session, city) for city in cities]
        await asyncio.gather(*tasks)

asyncio.run(main())

في الكود أعلاه، قمنا بإنشاء وظيفتين غير متزامنتين: واحدة لجلب البيانات من عنوان URL الخاص بالطقس المسبق والوظيفة الرئيسية لتنفيذ العمليات في كود Python. الهدف هو إرسال طلبات HTTP GET غير متزامنة لاسترداد درجات الحرارة وطباعة الردود.

في الوظائف الرئيسية ووظيفة الجلب، نستخدم المزامنة مع. في وظيفة الجلب، يضمن عدم المزامنة إغلاق الاتصال بشكل صحيح. في الوظيفة الرئيسية، يتم التأكد من إغلاق ClientSession بعد إكمال الطلبات. تعتبر هذه الممارسات مهمة في البرمجة غير المتزامنة في لغة بايثون لإدارة الموارد بكفاءة ومنع التسريبات.

في السطر الأخير من الوظيفة الرئيسية، نستخدم انتظار asyncio.gather(*tasks). في حالتنا، يقوم بتشغيل كافة المهام بشكل متزامن، مما يسمح للبرنامج بإرسال طلبات HTTP متعددة في وقت واحد. يضمن استخدام الانتظار أن ينتظر البرنامج حتى تكتمل جميع المهام قبل المتابعة.

الإخراج:

Temperature at marseille: 25 C
Temperature at toulouse: 24 C
Temperature at paris: 18 C
Program executed in 5.86 seconds.

نسخة متزامنة للمقارنة

شفرة:

import requests
import time

def fetch(city):
    url = f"https://www.prevision-meteo.ch/services/json/{city}"
    response = requests.get(url)
    data = response.json()
    print(f"Temperature at {city}: {data['current_condition']['tmp']} C")

def main():
    cities = ['paris', 'toulouse', 'marseille']
    for city in cities:
        fetch(city)

start_time = time.time()
main()
print(f"Program executed in {time.time() - start_time:.2f} seconds.")

الإخراج:

Temperature at Paris: 18 C
Temperature at Toulouse: 24 C
Temperature at Marseille: 25 C
Program executed in 9.01 seconds.

متى تستخدم البرمجة غير المتزامنة

يعمل النموذج غير المتزامن بشكل أفضل عندما:

  • هناك عدد كبير من المهام، مما يضمن إمكانية تقدم مهمة واحدة على الأقل دائمًا.

  • تتضمن المهام عمليات إدخال/إخراج كبيرة، مما يتسبب في إضاعة برنامج غير متزامن للكثير من الوقت في الحظر عندما يكون من الممكن تشغيل مهام أخرى.

  • المهام مستقلة إلى حد كبير، مما يقلل من التواصل بين المهام (وبالتالي انتظار مهمة واحدة لمهمة أخرى).

خاتمة

تناولنا في هذا البرنامج التعليمي ما يلي:

  • مفاهيم البرمجة غير المتزامنة والمفاهيم المتعلقة بها.

  • الاستخدام الفعال للمزامنة/الانتظار.

  • إنشاء طلبات HTTP غير متزامنة باستخدام aiohttp.

  • فوائد البرمجة غير المتزامنة.

شكرا على القراءة. الجزء الثاني سيغطي البرمجة غير المتزامنة مع جانغو.

موارد

  • وثائق بايثون: Coroutines والمهام

  • وثائق بايثون: غير متزامن - إدخال/إخراج غير متزامن

  • توثيق aiohttp

  • مكتبات Aio

  • التزامن مقابل التوازي

بيان الافراج تم نشر هذه المقالة على: https://dev.to/koladev/asynchronous-programming-with-asyncio-3ad1?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3