آموزش ماژول asyncio – مروری بر coroutine

Please login to bookmark Close

در مثال درست کردن غذا در آشپزخانه، این شما هستید که مشخص می‌کنید ژله و سالاد و غذا چه زمانی درست شوند، در صورتی که در مثال امور روزمره، شما مشخص نمی‌کنید که بسته چه زمانی به دست شما برسد یا اینکه گیاهان چه زمانی رشد کنند و محصول دهند.

در multithreading ما (سیستم‌عامل) مشخص می‌کنیم که ترتیب و چگونگی انجام تسک ها به چه شکل باشد اما در asyncio خود تسک ها هستند که مشخص می‌کنند چه زمانی انجام خواهند شد. به عبارتی دیگر در asyncio ما تخمین درستی از زمانی که تسک به پایان خواهد رسید نداریم زیرا انجام شدن تسک به یک عامل خارجی (مثلا یک سرور دیگر) وابسته است.

coroutine چیست؟

از نظر ظاهری یک تابع است که برای تعریف آن از دستور async و در بدنه آن حتما حداقل یک await دارد. به تکه کد زیر توجه کنید.

import asyncio

async def function_name();
    await asyncio.sleep(1)

توضیح کد بالا:

  • خط ۱: ماژول asyncio ایمپورت شده است تا بتوانیم از متد sleep آن استفاده کنیم.
  • خط ۳: با استفاده از دستور async یک coroutine تعریف کرده ایم.
  • خط ۴: با استفاده از دستور await مشخص کرده ایم که یک پاسخ از یک عامل خارجی قرار است برگردد. با استفاده از دستور asyncio.sleep اینطور شبیه سازی کرده ایم که گویی قرار است پاسخ از عامل خارجی پس از ۱ ثانیه برگردد

همانطور که مشاهده می‌کنید در تکه کد بالا از asyncio.sleep استفاده شده است. ما نمی‌توانیم از time.sleep استفاده کنیم زیرا متد sleep اش awaitable نیست.

توجه داشته باشید که دستور async و await ربطی به ماژول asyncio ندارند و توسط خود پایتون به صورت builtin قابل استفاده هستند.

اما از نظر مفهومی، coroutine یک تابع است که زمان خروجی دادنش به یک عامل خارجی بستگی دارد به صورتی که هر وقت آن عامل خارجی نتیجه مورد نظر را برگرداند، coroutine نیز با برگرداندن آن مقدار این را به ما اطلاع می‌دهد. این وابستگی ها به عامل خارجی با دستور await مشخص می‌شوند.

چطور می‌توان coroutine را اجرا کرد؟

از آنجایی که تعریف آن شبیه به تابع است شاید فکر کنید که می‌توان به صورت یک تابع به شکل زیر آن را فراخوانی کرد. به تکه کد زیر توجه کنید.

import asyncio

async def download_file(file_name):
    print(f'dowload {file_name} started')
    await asyncio.sleep(2)
    print(f'dowload {file_name} finished')

download_file('test.txt')

در صورتی که کد بالا را اجرا کنیم با warning زیر رو به رو خواهیم شد زیرا نباید یک coroutine را بدین شکل فراخوانی کرد.

RuntimeWarning: coroutine 'download_file' was never awaited
  download_file('test.txt')
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

اجرای یک coroutine سه روش دارد:

  • asyncio.run
  • asyncio.create_task + asyncio.run (روش پیشنهادی)
  • asyncio.new_event_loop + asyncio.run_until_complete

asyncio.run

یک coroutine را به صورت خطی اجرا می‌کند.

import asyncio

async def download_file(file_name):
    print(f'dowload {file_name} started')
    await asyncio.sleep(2)
    print(f'dowload {file_name} finished')

asyncio.run(download_file('test1.txt'))
asyncio.run(download_file('test2.txt'))

خروجی کد بالا می‌شود:

dowload test1.txt started
dowload test1.txt finished
dowload test2.txt started
dowload test2.txt finished

همانطور که مشخص است تسک ها به صورت همزمان اجرا نشده اند. قطعا این چیزی که ما از برنامه‌نویسی با asyncio می‌خواهیم نیست زیرا هدف ما اجرای همزمان درخواست هاست.

asyncio.create_task + asyncio.run

در این روش هر coroutine را در قالب یک task در یک coroutine اصلی اجرا خواهیم کرد.

import asyncio

async def download_file(file_name):
    print(f'dowload {file_name} started')
    await asyncio.sleep(2)
    print(f'dowload {file_name} finished')

async def manage_downloads():
    t1 = asyncio.create_task(download_file('test1.txt'))
    t2 = asyncio.create_task(download_file('test2.txt'))
    await t1
    await t2

asyncio.run(manage_downloads())

توضیح کد بالا:

  • خط ۳ تا ۶: یک coroutine است که نام یک فایل را میگیرد و آن را دانلود می‌کند.
  • خط ۸ تا ۱۲: یک coroutine است که coroutine های دیگر را اجرا می‌کند.
  • خط ۱۴: در این خط coroutine اصلی اجرا می‌شود.

asyncio.new_event_loop + asyncio.run_until_complete

در این روش خودمان event loop را ایجاد و اجرا می‌کنیم.

import asyncio

async def download_file(file_name):
    print(f'dowload {file_name} started')
    await asyncio.sleep(2)
    print(f'dowload {file_name} finished')

async def manage_downloads():
    t1 = asyncio.create_task(download_file('test1.txt'))
    t2 = asyncio.create_task(download_file('test2.txt'))
    await t1
    await t2

loop = asyncio.new_event_loop()
try:
    loop.run_until_complete(manage_downloads())
finally:
    loop.close()
Please login to bookmark Close
نظرات

دیدگاهتان را بنویسید

فهرست مطالب

سرفصل دوره

تمرین

این قسمت تمرین ندارد!

پاسخ تمرین ها

هنوز برای تمرین‌های این قسمت پاسخی ثبت نشده است!

اشتراک گذاری

چرا بهتره از فیلترشکن استفاده کنید؟

من همه ویدئو ها و پادکست های کُدباز رو توی یوتیوب و ساندکلود و پلتفرم هایی آپلود می‌کنم که اغلب فیلتر هستند.

اغلب آموزش‌ها ویدئو و پادکست دارند. پس اگر می‌خواهید از محتوای سایت بیشترین استفاده رو ببرید نیاز به فیلتر شکن دارید.

توجه داشته باشید که برای خرید از فروشگاه بهتره فیلتر شکن رو خاموش کنید.

تنظیمات

انتخاب زبان
تغییر تم