اولیههای همگامسازی¶
کد منبع: Lib/asyncio/locks.py
اولیههای همگامسازی asyncio بهگونهای طراحی شدهاند که شبیه به همتاهای خود در ماژول threading باشند، با دو نکتهی مهم:
اولیههای asyncio ایمن از نظر نخ (thread-safe) نیستند، بنابراین نباید برای همگامسازی نخهای سیستمعامل از آنها استفاده شود (برای این کار از
threadingاستفاده کنید)؛متدهای این اولیههای همگامسازی آرگومان timeout را نمیپذیرند؛ برای انجام عملیات با مهلتهای زمانی از تابع
asyncio.wait_for()استفاده کنید.
asyncio دارای اولیههای پایهی همگامسازی زیر است:
قفل¶
- class asyncio.Lock¶
یک قفل متقابل (mutex lock) برای وظایف asyncio پیادهسازی میکند. ایمن از نظر نخ (thread-safe) نیست.
میتوان از یک قفل asyncio برای تضمین دسترسی انحصاری به یک منبع مشترک استفاده کرد.
روش ترجیحی برای استفاده از Lock، دستور
async withاست:lock = asyncio.Lock() # ... later async with lock: # access shared state
که معادل زیر است:
lock = asyncio.Lock() # ... later await lock.acquire() try: # access shared state finally: lock.release()
تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
- async acquire()¶
قفل را کسب کنید.
این متد منتظر میماند تا قفل باز شود، آن را قفل میکند و
Trueرا برمیگرداند.هنگامی که بیش از یک همروال در
acquire()مسدود شده باشد و منتظر باز شدن قفل باشد، در نهایت تنها یک همروال ادامه پیدا میکند.کسب قفل منصفانه است: همروالی که ادامه میدهد، نخستین همروالی خواهد بود که منتظر قفل شده است.
- release()¶
قفل را آزاد کنید.
هنگامی که قفل قفلشده است، آن را به باز بازنشانی کنید و بازگردید.
اگر قفل باز باشد، یک
RuntimeErrorپرتاب میشود.
- locked()¶
اگر قفل قفلشده باشد،
Trueرا برمیگرداند.
رویداد¶
- class asyncio.Event¶
یک شیء رویداد. نخایمن نیست.
میتوان از یک رویداد asyncio برای آگاهسازی چندین وظیفه در asyncio از وقوع رویدادی استفاده کرد.
یک شیء Event یک پرچم داخلی را مدیریت میکند که میتوان آن را با متد
set()روی true تنظیم کرد و با متدclear()به false بازنشانی کرد. متدwait()تا زمانی که پرچم روی true تنظیم شود، مسدود میشود. پرچم در ابتدا روی false تنظیم شده است.تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
مثال:
async def waiter(event): print('waiting for it ...') await event.wait() print('... got it!') async def main(): # Create an Event object. event = asyncio.Event() # Spawn a Task to wait until 'event' is set. waiter_task = asyncio.create_task(waiter(event)) # Sleep for 1 second and set the event. await asyncio.sleep(1) event.set() # Wait until the waiter task is finished. await waiter_task asyncio.run(main())
- async wait()¶
صبر کنید تا رویداد تنظیم شود.
اگر رویداد تنظیمشده باشد، بلافاصله
Trueرا برمیگرداند. در غیر این صورت، تا زمانی که وظیفه دیگریset()را فراخوانی کند، مسدود میشود.
- set()¶
رویداد را تنظیم کنید.
همهی وظایفی که در انتظار تنظیم شدن رویداد هستند، بیدرنگ بیدار خواهند شد.
- clear()¶
رویداد را پاک (لغو تنظیم) میکند.
وظایف بعدی که
wait()را await میکنند، اکنون تا زمانی که متدset()دوباره فراخوانی شود، مسدود خواهند شد.
- is_set()¶
اگر رویداد تنظیمشده باشد،
Trueرا برمیگرداند.
شرایط¶
- class asyncio.Condition(lock=None)¶
یک شیء Condition. ایمن از نظر نخ نیست.
یک وظیفه میتواند از یک سازوکار شرطی اولیه در asyncio استفاده کند تا برای رخ دادن رویدادی منتظر بماند و سپس دسترسی انحصاری به یک منبع مشترک را بهدست آورد.
در اصل، یک شیء Condition، کارکرد
EventوLockرا ترکیب میکند. میتوان چندین شیء Condition داشت که یک Lock را به اشتراک میگذارند؛ این امکان، هماهنگسازی دسترسی انحصاری به یک منبع مشترک را بین وظایف مختلفی که به وضعیتهای خاصی از آن منبع مشترک علاقهمند هستند، ممکن میسازد.آرگومان اختیاری lock باید یک شیء
LockیاNoneباشد. در حالت دوم، یک شیء Lock جدید بهصورت خودکار ایجاد میشود.تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
روش ترجیحدادهشده برای استفاده از Condition، دستور
async withاست:cond = asyncio.Condition() # ... later async with cond: await cond.wait()
که معادل زیر است:
cond = asyncio.Condition() # ... later await cond.acquire() try: await cond.wait() finally: cond.release()
- async acquire()¶
قفل زیربنایی را تصاحب کنید.
این متد تا زمانی که قفل زیربنایی آزاد شود صبر میکند، آن را در حالت قفلشده قرار میدهد و
Trueرا برمیگرداند.
- notify(n=1)¶
بیدار کردن n وظیفه در انتظار این شرط (بهطور پیشفرض ۱). اگر کمتر از n وظیفه در انتظار باشند، همهی آنها بیدار میشوند.
قفل باید پیش از فراخوانی این متد کسب شود و اندکی پس از آن آزاد شود. اگر با یک قفل باز فراخوانی شود، خطای
RuntimeErrorپرتاب میشود.
- locked()¶
اگر قفل زیربنایی کسب شده باشد،
Trueبرمیگرداند.
- notify_all()¶
تمام وظایف در انتظار این شرط را بیدار کنید.
این متد مانند
notify()عمل میکند، اما تمام وظایف در انتظار را بیدار میکند.قفل باید پیش از فراخوانی این متد کسب شود و اندکی پس از آن آزاد شود. اگر با یک قفل باز فراخوانی شود، خطای
RuntimeErrorپرتاب میشود.
- release()¶
قفل زیربنایی را آزاد کنید.
هنگامی که روی یک قفل باز فراخوانی شود، یک
RuntimeErrorپرتاب میشود.
- async wait()¶
تا زمانی که به شما اطلاع داده شود، صبر کنید.
اگر در زمان فراخوانی این متد، وظیفه فراخوانیکننده قفل را به دست نیاورده باشد، یک
RuntimeErrorپرتاب میشود.این متد قفل زیربنایی را آزاد میکند و سپس تا زمانی که با فراخوانی
notify()یاnotify_all()بیدار شود، مسدود میشود. پس از بیدار شدن، Condition قفل خود را دوباره به دست میآورد و این متدTrueرا برمیگرداند.توجه داشته باشید که یک وظیفه ممکن است بهصورت کاذب از این فراخوانی بازگردد، به همین دلیل فراخواننده باید همیشه وضعیت را دوباره بررسی کند و برای فراخوانی دوبارهی
wait()آماده باشد. به همین دلیل، ممکن است ترجیح دهید بهجای آن ازwait_for()استفاده کنید.
سمافور¶
- class asyncio.Semaphore(value=1)¶
یک شیء Semaphore. نخایمن نیست.
سمافور یک شمارندهی داخلی را مدیریت میکند که با هر فراخوانی
acquire()کاهش مییابد و با هر فراخوانیrelease()افزایش مییابد. شمارنده هرگز نمیتواند زیر صفر برود؛ هنگامی کهacquire()ببیند که شمارنده صفر است، مسدود میشود و منتظر میماند تا وظیفهیrelease()را فراخوانی کند.آرگومان اختیاری value مقدار اولیه شمارنده داخلی را مشخص میکند (بهطور پیشفرض
1). اگر مقدار دادهشده کمتر از0باشد، یکValueErrorپرتاب میشود.تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
روش ترجیحی برای استفاده از Semaphore، استفاده از یک دستور
async withاست:sem = asyncio.Semaphore(10) # ... later async with sem: # work with shared resource
که معادل زیر است:
sem = asyncio.Semaphore(10) # ... later await sem.acquire() try: # work with shared resource finally: sem.release()
- async acquire()¶
یک سمافور را تصاحب کنید.
اگر شمارندهی داخلی بزرگتر از صفر باشد، آن را یک واحد کاهش دهید و بلافاصله
Trueرا برگردانید. اگر صفر باشد، منتظر بمانید تاrelease()فراخوانی شود وTrueرا برگردانید.
- locked()¶
اگر سمافور نتواند بلافاصله کسب شود،
Trueرا برمیگرداند.
- release()¶
یک سمافور را آزاد میکند و شمارندهی داخلی را ۱ واحد افزایش میدهد. میتواند وظیفهای را که در انتظار بهدست آوردن سمافور است بیدار کند.
برخلاف
BoundedSemaphore،Semaphoreاجازه میدهد فراخوانیهایrelease()بیشتری نسبت به فراخوانیهایacquire()انجام شود.
BoundedSemaphore¶
- class asyncio.BoundedSemaphore(value=1)¶
یک شیء سمافور کراندار. نخایمن نیست.
سمافور محدود (Bounded Semaphore) نسخهای از
Semaphoreاست که درrelease()، اگر شمارنده داخلی را به بالاتر از value اولیه افزایش دهد،ValueErrorپرتاب میکند.تغییر یافته در نسخهی 3.10: پارامتر loop حذف شد.
سد¶
- class asyncio.Barrier(parties)¶
یک شیء سد (barrier). نخایمن نیست.
یک سد، یک ساختار اولیه همگامسازی ساده است که امکان مسدود شدن تا زمانی که تعداد parties وظیفه منتظر آن باشند را فراهم میکند. وظایف میتوانند با متد
wait()منتظر بمانند و تا زمانی که تعداد مشخصشدهای از وظایف در نهایت با متدwait()منتظر شوند، مسدود میمانند. در آن نقطه، همه وظایف منتظر بهطور همزمان از حالت مسدود خارج میشوند.async withمیتواند بهعنوان جایگزینی برای await رویwait()استفاده شود.این سد را میتوان هر تعداد باری دوباره استفاده کرد.
مثال:
async def example_barrier(): # barrier with 3 parties b = asyncio.Barrier(3) # create 2 new waiting tasks asyncio.create_task(b.wait()) asyncio.create_task(b.wait()) await asyncio.sleep(0) print(b) # The third .wait() call passes the barrier await b.wait() print(b) print("barrier passed") await asyncio.sleep(0) print(b) asyncio.run(example_barrier())
نتیجهی این مثال:
<asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]> <asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]> barrier passed <asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>
اضافه شده در نسخهی 3.11.
- async wait()¶
از سد عبور کنید. هنگامی که همه وظایف شرکتکننده در سد این تابع را فراخوانی کرده باشند، همه آنها بهطور همزمان رفع انسداد میشوند.
هنگامی که یک وظیفهی در انتظار یا مسدودشده در سد لغو شود، این وظیفه از سد خارج میشود و سد در همان وضعیت باقی میماند. اگر وضعیت سد «در حال پر شدن» باشد، تعداد وظایف در انتظار به میزان ۱ کاهش مییابد.
مقدار بازگشتی یک عدد صحیح در بازهی ۰ تا
parties-1است و برای هر وظیفه متفاوت است. میتوان از این مقدار برای انتخاب یک وظیفه جهت انجام برخی عملیات نگهداری خاص استفاده کرد، برای مثال:... async with barrier as position: if position == 0: # Only one task prints this print('End of *draining phase*')
این متد ممکن است در صورتی که سد در حین انتظار یک وظیفه شکسته شود یا بازنشانی شود، استثنای
BrokenBarrierErrorرا پرتاب کند. ممکن است در صورت لغو شدن یک وظیفه، استثنایCancelledErrorرا پرتاب کند.
- async reset()¶
سد را به حالت پیشفرض و خالی بازمیگرداند. هر وظیفهی که در انتظار آن باشد، استثنای
BrokenBarrierErrorرا دریافت خواهد کرد.اگر یک سد (barrier) شکسته شود، ممکن است بهتر باشد آن را به حال خود رها کنید و یک سد جدید ایجاد کنید.
- async abort()¶
سد (Barrier) را در وضعیت شکسته قرار دهید. این کار باعث میشود هر فراخوانی فعال یا آینده به
wait()باBrokenBarrierErrorناموفق شود. برای مثال، اگر یکی از وظایف نیاز به لغو شدن داشته باشد، از این استفاده کنید تا از وظایفی که بهطور بیپایان در انتظار میمانند جلوگیری شود.
- parties¶
تعداد وظایف لازم برای عبور از سد.
- n_waiting¶
تعداد وظایفی که در حال حاضر در حین پر شدن سد (barrier) منتظر هستند.
- broken¶
یک بولی که اگر سد در وضعیت شکسته باشد،
Trueاست.
- exception asyncio.BrokenBarrierError¶
این استثنا، که زیرکلاسی از
RuntimeErrorاست، زمانی پرتاب میشود که شیءBarrierبازنشانی یا شکسته شود.
تغییر یافته در نسخهی 3.9: کسب قفل با استفاده از await lock یا yield from lock و/یا دستور with (with await lock، with (yield from lock)) حذف شده است. به جای آن از async with lock استفاده کنید.