queue --- کلاس صف همگامشده¶
کد منبع: Lib/queue.py
ماژول queue صفهایی با چند تولیدکننده و چند مصرفکننده را پیادهسازی میکند. این ماژول بهویژه در برنامهنویسی نخی، زمانی که اطلاعات باید بهصورت ایمن بین چند نخ مبادله شود، مفید است. کلاس Queue در این ماژول، تمام معناهای قفلسازی مورد نیاز را پیادهسازی میکند.
این ماژول سه نوع صف را پیادهسازی میکند که تنها در ترتیب بازیابی ورودیها تفاوت دارند. در صف FIFO، نخستین وظایف افزودهشده، نخستین وظایف بازیابیشده هستند. در صف LIFO، آخرین ورودی افزودهشده، نخستین ورودی بازیابیشده است (مانند یک هیپ عمل میکند). در صف اولویت، ورودیها مرتب نگه داشته میشوند (با استفاده از ماژول heapq) و ورودی با کمترین مقدار، ابتدا بازیابی میشود.
در داخل، آن سه نوع صف از قفلها استفاده میکنند تا نخهای رقیب را بهطور موقت مسدود کنند؛ با این حال، آنها برای مدیریت بازورودپذیری (reentrancy) درون یک نخ طراحی نشدهاند.
علاوه بر این، این ماژول یک نوع صف «ساده» از نوع FIFO، SimpleQueue، را پیادهسازی میکند که پیادهسازی خاص آن، تضمینهای بیشتری را در ازای قابلیت کمتر ارائه میدهد.
ماژول queue کلاسها و استثناهای زیر را تعریف میکند:
- class queue.Queue(maxsize=0)¶
سازندهی یک صف FIFO. maxsize یک عدد صحیح است که حد بالای تعداد آیتمهایی را که میتوان در صف قرار داد، تعیین میکند. پس از رسیدن به این اندازه، درج مسدود میشود تا زمانی که آیتمهای صف مصرف شوند. اگر maxsize کوچکتر یا مساوی صفر باشد، اندازه صف بینهایت است.
- class queue.LifoQueue(maxsize=0)¶
سازندهای برای صف LIFO (آخرین ورودی، اولین خروجی) <LIFO (last-in, first-out)>. maxsize یک عدد صحیح است که حد بالای تعداد آیتمهایی را که میتوان در صف قرار داد، تعیین میکند. پس از رسیدن به این اندازه، درج تا زمانی که آیتمهای صف مصرف شوند، مسدود خواهد شد. اگر maxsize کمتر یا مساوی صفر باشد، اندازه صف بینهایت است.
- class queue.PriorityQueue(maxsize=0)¶
سازنده برای یک صف اولویتدار. maxsize یک عدد صحیح است که حد بالایی تعداد آیتمهایی را که میتوان در صف قرار داد تعیین میکند. پس از رسیدن به این اندازه، درج تا زمانی که آیتمهای صف مصرف شوند مسدود میشود. اگر maxsize کمتر یا مساوی صفر باشد، اندازه صف بینهایت است.
ابتدا آیتمهایی با کمترین مقدار بازیابی میشوند (آیتم با کمترین مقدار، آیتمی است که
min(entries)آن را بازمیگرداند). یک الگوی رایج برای آیتمها، تاپلبه شکل(priority_number, data)است.اگر عناصر data قابل مقایسه نباشند، میتوان داده را در کلاسی پوششی قرار داد که آیتم داده را نادیده میگیرد و تنها عدد اولویت را مقایسه میکند:
from dataclasses import dataclass, field from typing import Any @dataclass(order=True) class PrioritizedItem: priority: int item: Any=field(compare=False)
- class queue.SimpleQueue¶
سازنده برای یک صف نامحدود FIFO. صفهای ساده فاقد قابلیتهای پیشرفتهای مانند پیگیری وظایف هستند.
صفهای ساده نسبت به نوع آیتمهای خود generic هستند.
اضافه شده در نسخهی 3.7.
- exception queue.Empty¶
استثنایی که هنگام فراخوانی
get()بهصورت غیرمسدودکننده (یاget_nowait()) روی یک شیءQueueکه خالی است، پرتاب میشود.
- exception queue.Full¶
استثنایی که هنگام فراخوانی
put()(یاput_nowait()) بهصورت غیرمسدودکننده بر روی یک شیءQueueکه پر است، پرتاب میشود.
- exception queue.ShutDown¶
استثنایی که هنگام فراخوانی
put()یاget()روی یک شیءQueueکه خاموش شده است، پرتاب میشود.اضافه شده در نسخهی 3.13.
اشیاء صف¶
اشیای صف (Queue، LifoQueue یا PriorityQueue) متدهای عمومی توصیفشده در زیر را ارائه میدهند.
- Queue.qsize()¶
اندازهی تقریبی صف را برمیگرداند. توجه داشته باشید که qsize() > 0 تضمین نمیکند که get() بعدی مسدود نخواهد شد، و qsize() < maxsize نیز تضمین نمیکند که put() مسدود نخواهد شد.
- Queue.empty()¶
اگر صف خالی باشد،
Trueو در غیر این صورتFalseبرمیگرداند. اگر empty() مقدارTrueبرگرداند، تضمین نمیکند که فراخوانی بعدی put() مسدود نخواهد شد. بهطور مشابه، اگر empty() مقدارFalseبرگرداند، تضمین نمیکند که فراخوانی بعدی get() مسدود نخواهد شد.
- Queue.full()¶
اگر صف پر باشد،
Trueو در غیر این صورتFalseبرمیگرداند. اگر full() مقدارTrueرا برگرداند، تضمین نمیکند که فراخوانی بعدی get() مسدود نخواهد شد. بهطور مشابه، اگر full() مقدارFalseرا برگرداند، تضمین نمیکند که فراخوانی بعدی put() مسدود نخواهد شد.
- Queue.put(item, block=True, timeout=None)¶
item را در صف قرار میدهد. اگر آرگومان اختیاری block درست باشد و timeout برابر
Noneباشد (پیشفرض)، در صورت لزوم تا زمانی که یک جایگاه آزاد در دسترس قرار گیرد، مسدود میشود. اگر timeout یک عدد مثبت باشد، حداکثر timeout ثانیه مسدود میشود و اگر در آن مدت جایگاه آزادی در دسترس قرار نگرفت، استثنایFullرا پرتاب میکند. در غیر این صورت (block نادرست است)، اگر یک جایگاه آزاد بلافاصله در دسترس باشد، یک آیتم را در صف قرار میدهد، وگرنه استثنایFullرا پرتاب میکند (در این حالت timeout نادیده گرفته میشود).اگر صف خاموششده باشد،
ShutDownرا پرتاب میکند.
- Queue.put_nowait(item)¶
معادل با
put(item, block=False).
- Queue.get(block=True, timeout=None)¶
یک آیتم را از صف حذف و برمیگرداند. اگر آرگومانهای اختیاری به این صورت باشند که block درست باشد و timeout برابر
Noneباشد (پیشفرض)، در صورت لزوم تا زمانی که یک آیتم در دسترس باشد مسدود میشود. اگر timeout یک عدد مثبت باشد، حداکثر به مدت timeout ثانیه مسدود میشود و اگر هیچ آیتمی در آن مدت در دسترس نباشد، استثنایEmptyرا پرتاب میکند. در غیر این صورت (یعنی وقتی block نادرست باشد)، اگر یک آیتم بلافاصله در دسترس باشد آن را برمیگرداند، وگرنه استثنایEmptyرا پرتاب میکند (در این حالت timeout نادیده گرفته میشود).پیش از 3.0 در سیستمهای POSIX، و برای تمام نسخهها در ویندوز، اگر block درست باشد و timeout برابر
Noneباشد، این عملیات وارد یک انتظار غیرقابلوقفه برای یک قفل زیربنایی میشود. این بدان معناست که هیچ استثنایی نمیتواند رخ دهد، و بهویژه، یک SIGINT موجبKeyboardInterruptنخواهد شد.اگر صف خاموش شده باشد و خالی باشد، یا اگر صف بهصورت فوری خاموش شده باشد،
ShutDownرا پرتاب میکند.
- Queue.get_nowait()¶
معادل
get(False).
دو متد برای پشتیبانی از پیگیری این موضوع ارائه شده است که آیا وظایف در صف قرار گرفته بهطور کامل توسط نخهای مصرفکنندهی دِیمِن (daemon) پردازش شدهاند یا خیر.
- Queue.task_done()¶
نشان میدهد که وظیفهای که پیشتر در صف قرار گرفته، کامل شده است. توسط نخهای مصرفکنندهی صف استفاده میشود. به ازای هر
get()که برای دریافت یک وظیفه استفاده میشود، فراخوانی متعاقبtask_done()به صف اطلاع میدهد که پردازش روی آن وظیفه کامل شده است.اگر یک
join()در حال حاضر مسدود شده باشد، هنگامی که همهی آیتمها پردازش شده باشند، ادامه مییابد (به این معنا که یک فراخوانیtask_done()برای هر آیتمی که باput()در صف قرار داده شده بود، دریافت شده باشد).اگر بیش از تعداد آیتمهای قرار دادهشده در صف فراخوانی شود، یک
ValueErrorپرتاب میکند.
- Queue.join()¶
مسدود میشود تا همهی آیتمهای موجود در صف دریافت و پردازش شده باشند.
تعداد وظایف ناتمام هر بار که یک آیتم به صف اضافه شود، افزایش مییابد. این تعداد هر بار که یک نخ مصرفکننده
task_done()را فراخوانی میکند، کاهش مییابد تا نشان دهد آن آیتم دریافت شده و تمام کار روی آن کامل شده است. هنگامی که تعداد وظایف ناتمام به صفر برسد،join()از حالت مسدود خارج میشود.
انتظار برای تکمیل وظیفه¶
مثالی از نحوهی انتظار برای تکمیل شدن وظایف در صف:
import threading
import queue
q = queue.Queue()
def worker():
while True:
item = q.get()
print(f'Working on {item}')
print(f'Finished {item}')
q.task_done()
# Turn-on the worker thread.
threading.Thread(target=worker, daemon=True).start()
# Send thirty task requests to the worker.
for item in range(30):
q.put(item)
# Block until all tasks are done.
q.join()
print('All work completed')
پایان دادن به صفها¶
هنگامی که دیگر نیازی به آنها نیست، اشیاء Queue میتوانند تا خالی شدن بهتدریج متوقف شوند یا بیدرنگ با یک خاموشی سخت (hard shutdown) خاتمه یابند.
- Queue.shutdown(immediate=False)¶
یک نمونه
Queueرا در حالت خاموشی قرار دهید.صف دیگر نمیتواند رشد کند. فراخوانیهای آینده به
put()،ShutDownرا پرتاب خواهند کرد. فراخوانیکنندگان مسدودشدهی کنونیput()از انسداد خارج خواهند شد وShutDownرا در نخ پیشتر مسدودشده پرتاب خواهند کرد.اگر immediate نادرست باشد (پیشفرض)، میتوان صف را بهصورت عادی با فراخوانیهای
get()برای استخراج وظایفی که از قبل بارگذاری شدهاند، تخلیه کرد.و اگر
task_done()برای هر وظیفهی باقیمانده فراخوانی شود، یکjoin()در انتظار بهطور عادی رفع انسداد خواهد شد.همین که صف خالی شد، فراخوانیهای بعدی به
get()،ShutDownرا پرتاب خواهند کرد.اگر immediate مقدار true داشته باشد، صف بلافاصله خاتمه داده میشود. صف تخلیه میشود تا کاملاً خالی شود و تعداد وظایف تمامنشده به اندازه تعداد وظایف تخلیهشده کاهش مییابد. اگر تعداد وظایف تمامنشده صفر باشد، فراخوانندگان
join()از حالت مسدود خارج میشوند. همچنین، فراخوانندگان مسدودشدهیget()از حالت مسدود خارج میشوند وShutDownرا پرتاب خواهند کرد، زیرا صف خالی است.هنگام استفاده از
join()در صورتی که immediate روی true تنظیمشده باشد، احتیاط کنید. این کار حتی زمانی که هیچ کاری روی وظایف انجامنشده باشد، join را از حالت مسدود خارج میکند و ناوردایی معمول برای پیوستن به یک صف را نقض میکند.اضافه شده در نسخهی 3.13.
اشیای SimpleQueue¶
اشیای SimpleQueue متدهای عمومی توصیفشده در زیر را ارائه میدهند.
- SimpleQueue.qsize()¶
اندازهی تقریبی صف را برمیگرداند. توجه داشته باشید که qsize() > 0 تضمین نمیکند که get() بعدی مسدود نخواهد شد.
- SimpleQueue.empty()¶
اگر صف خالی باشد،
Trueو در غیر این صورتFalseرا برمیگرداند. اگر empty() مقدارFalseرا برگرداند، تضمینی وجود ندارد که فراخوانی بعدی به get() مسدود نشود.
- SimpleQueue.put(item, block=True, timeout=None)¶
item را در صف قرار میدهد. این متد هرگز مسدود نمیشود و همیشه با موفقیت انجام میشود (بهجز خطاهای احتمالی سطح پایین مانند ناتوانی در تخصیص حافظه). آرگومانهای اختیاری block و timeout نادیده گرفته میشوند و تنها برای سازگاری با
Queue.put()ارائه شدهاند.این متد یک پیادهسازی C دارد که بازورودپذیر (reentrant) دارد. یعنی یک فراخوانی
put()یاget()میتواند توسط یک فراخوانیput()دیگر در همان نخ، بدون ایجاد بنبست (deadlock) یا خراب کردن وضعیت داخلی صف، قطع شود. این موضوع آن را برای استفاده در تخریبکنندهها (destructors)، مانند متدهای__del__یا کالبکهایweakrefمناسب میسازد.
- SimpleQueue.put_nowait(item)¶
معادل
put(item, block=False)است و برای سازگاری باQueue.put_nowait()ارائه شده است.
- SimpleQueue.get(block=True, timeout=None)¶
یک آیتم را از صف حذف و برمیگرداند. اگر آرگومانهای اختیاری به این صورت باشند که block درست باشد و timeout برابر
Noneباشد (پیشفرض)، در صورت لزوم تا زمانی که یک آیتم در دسترس باشد مسدود میشود. اگر timeout یک عدد مثبت باشد، حداکثر به مدت timeout ثانیه مسدود میشود و اگر هیچ آیتمی در آن مدت در دسترس نباشد، استثنایEmptyرا پرتاب میکند. در غیر این صورت (یعنی وقتی block نادرست باشد)، اگر یک آیتم بلافاصله در دسترس باشد آن را برمیگرداند، وگرنه استثنایEmptyرا پرتاب میکند (در این حالت timeout نادیده گرفته میشود).
- SimpleQueue.get_nowait()¶
معادل
get(False).
همچنین ملاحظه نمائید
- کلاس
multiprocessing.Queue یک کلاس صف برای استفاده در زمینهی چندفرایندی (multi-processing) بهجای چندنخی (multi-threading).
collections.deque یک پیادهسازی جایگزین از صفهای نامحدود است که عملیات اتمی سریع append() و popleft() را بدون نیاز به قفلگذاری فراهم میکند و از اندیسدهی نیز پشتیبانی میکند.