threading --- موازیسازی مبتنی بر نخ¶
کد منبع: Lib/threading.py
این ماژول رابطهای نخبندی سطح بالاتر را بر روی ماژول سطح پایینتر _thread ایجاد میکند.
دسترسپذیری: not WASI.
این ماژول روی WebAssembly کار نمیکند یا در دسترس نیست. برای اطلاعات بیشتر، سکوهای WebAssembly را ببینید.
مقدمه¶
ماژول threading راهی برای اجرای همزمان چندین نخ (واحدهای کوچکتری از یک فرآیند) در یک فرآیند واحد فراهم میکند. این ماژول امکان ایجاد و مدیریت نخها را فراهم میکند و اجرای وظایف را بهصورت موازی، با اشتراکگذاری فضای حافظه، ممکن میسازد. نخها بهویژه زمانی مفید هستند که وظایف محدود به I/O باشند، مانند عملیات پرونده یا ارسال درخواستهای شبکه، که بخش زیادی از زمان صرف انتظار برای منابع خارجی میشود.
یک مورد استفاده رایج برای threading شامل مدیریت استخری از نخهای کاری است که میتوانند چندین وظیفه را بهطور همزمان پردازش کنند. در ادامه یک مثال پایه از ایجاد و شروع نخها با استفاده از Thread آمده است:
import threading
import time
def crawl(link, delay=3):
print(f"crawl started for {link}")
time.sleep(delay) # Blocking I/O (simulating a network request)
print(f"crawl ended for {link}")
links = [
"https://python.org",
"https://docs.python.org",
"https://peps.python.org",
]
# Start threads for each link
threads = []
for link in links:
# Using `args` to pass positional arguments and `kwargs` for keyword arguments
t = threading.Thread(target=crawl, args=(link,), kwargs={"delay": 2})
threads.append(t)
# Start each thread
for t in threads:
t.start()
# Wait for all threads to finish
for t in threads:
t.join()
تغییر یافته در نسخهی 3.7: این ماژول پیشتر اختیاری بود، اما اکنون همیشه در دسترس است.
همچنین ملاحظه نمائید
concurrent.futures.ThreadPoolExecutor رابط سطح بالاتری را برای فرستادن وظایف به یک نخ پسزمینه بدون مسدود شدن اجرای نخ فراخواننده ارائه میدهد، در حالی که همچنان میتوان نتایج آنها را در زمان نیاز بازیابی کرد.
queue یک رابط ایمن از نظر نخی برای تبادل داده بین نخهای در حال اجرا فراهم میکند.
asyncio رویکرد جایگزینی برای دستیابی به همروندی در سطح وظیفه ارائه میدهد، بدون نیاز به استفاده از چندین نخ سیستمعامل.
توجه
در سری Python 2.x، این ماژول شامل نامهای camelCase برای برخی متدها و توابع بود. این موارد از Python 3.10 منسوخ شدهاند، اما همچنان برای سازگاری با Python 2.5 و نسخههای پایینتر پشتیبانی میشوند.
در CPython، به دلیل قفل مفسر سراسری، تنها یک نخ میتواند کد پایتون را در یک لحظه اجرا کند (هرچند ممکن است برخی کتابخانههای کاراییمحور بر این محدودیت غلبه کنند). اگر میخواهید برنامه شما استفاده بهتری از منابع محاسباتی ماشینهای چندهستهای بکند، توصیه میشود از multiprocessing یا concurrent.futures.ProcessPoolExecutor استفاده کنید. با این حال، اگر میخواهید چندین وظیفه محدود به I/O را بهطور همزمان اجرا کنید، نخبندی همچنان مدل مناسبی است.
ملاحظات GIL و کارایی¶
برخلاف ماژول multiprocessing، که از فرآیندهای جداگانه برای دور زدن global interpreter lock (GIL) استفاده میکند، ماژول threading در یک فرآیند واحد کار میکند، به این معنا که همه نخها فضای حافظه یکسانی را به اشتراک میگذارند. با این حال، GIL بهبود عملکرد حاصل از نخبندی را در وظایف محدود به CPU محدود میکند، زیرا در هر لحظه تنها یک نخ میتواند بایتکد پایتون را اجرا کند. با وجود این، نخها همچنان ابزاری مفید برای دستیابی به همروندی در بسیاری از سناریوها هستند.
از پایتون 3.13 به بعد، ساختهای free-threaded میتوانند GIL را غیرفعال کنند و امکان اجرای موازی واقعی نخها را فراهم کنند، اما این قابلیت بهطور پیشفرض در دسترس نیست (به PEP 703 مراجعه کنید).
مرجع¶
این ماژول توابع زیر را تعریف میکند:
- threading.active_count()¶
تعداد اشیای
Threadرا که در حال حاضر زندهاند برمیگرداند. تعداد برگرداندهشده برابر با طول فهرست برگرداندهشده توسطenumerate()است.تابع
activeCountیک نام مستعار منسوخ برای این تابع است.
- threading.current_thread()¶
شیء جاری
Threadرا، که متناظر با نخ کنترل فراخواننده است، برمیگرداند. اگر نخ کنترل فراخواننده از طریق ماژولthreadingایجاد نشده باشد، یک شیء نخ ساختگی با کارکرد محدود برگردانده میشود.تابع
currentThreadیک نام مستعار منسوخ برای این تابع است.
- threading.excepthook(args, /)¶
مدیریت استثنای گرفتهنشده که توسط
Thread.run()پرتاب میشود.آرگومان args دارای ویژگیهای زیر است:
exc_type: نوع استثنا.
exc_value: مقدار استثنا، میتواند
Noneباشد.exc_traceback: ردگیری پشتهی استثنا، میتواند
Noneباشد.thread: نخی که استثنا را پرتاب کرده است، میتواند
Noneباشد.
اگر exc_type
SystemExitباشد، استثنا بهصورت بیصدا نادیده گرفته میشود. در غیر این صورت، استثنا درsys.stderrچاپ میشود.اگر این تابع استثنایی را پرتاب کند، برای مدیریت آن
sys.excepthook()فراخوانی میشود.میتوان
threading.excepthook()را بازنویسی کرد تا چگونگی رسیدگی به استثناهای گرفتهنشدهای که توسطThread.run()پرتاب میشوند، کنترل شود.ذخیرهسازی exc_value با استفاده از یک قلاب سفارشی میتواند یک چرخه ارجاع ایجاد کند. هنگامی که دیگر به استثنا نیازی نیست، باید آن را بهصراحت پاک کرد تا چرخه ارجاع شکسته شود.
ذخیرهسازی thread با استفاده از یک قلاب سفارشی، در صورتی که به شیءای که در حال نهاییشدن است تنظیم شده باشد، میتواند باعث احیای آن شود. برای جلوگیری از احیای اشیاء، از ذخیرهسازی thread پس از تکمیل قلاب سفارشی خودداری کنید.
همچنین ملاحظه نمائید
sys.excepthook()به استثناهای گرفتهنشده رسیدگی میکند.اضافه شده در نسخهی 3.8.
- threading.__excepthook__¶
مقدار اصلی
threading.excepthook()را نگه میدارد. این مقدار ذخیره شده است تا بتوان مقدار اصلی را در صورتی که بهطور اتفاقی با اشیاء خراب یا جایگزین تعویض شده باشد، بازیابی کرد.اضافه شده در نسخهی 3.10.
- threading.get_ident()¶
«شناسه نخ» را برای نخ جاری برمیگرداند. این یک عدد صحیح غیرصفر است. مقدار آن معنای مستقیمی ندارد؛ بلکه بهعنوان یک کوکی جادویی (magic cookie) در نظر گرفته شده است تا مثلاً برای اندیسدهی به یک دیکشنری از دادههای مختص نخ استفاده شود. شناسههای نخ ممکن است هنگامی که یک نخ خارج میشود و نخ دیگری ایجاد میشود، بازیافت شوند.
اضافه شده در نسخهی 3.3.
- threading.get_native_id()¶
شناسهی نخ (Thread ID) بومی بهصورت عدد صحیح برای نخ جاری را که توسط کرنل اختصاص داده شده است، برمیگرداند. این مقدار، یک عدد صحیح نامنفی است. میتوان از مقدار آن برای شناسایی یکتای این نخ خاص در سراسر سیستم استفاده کرد (تا زمانی که نخ خاتمه یابد؛ پس از آن ممکن است این مقدار توسط سیستمعامل بازیافت شود).
دسترسپذیری: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD.
اضافه شده در نسخهی 3.8.
تغییر یافته در نسخهی 3.13: پشتیبانی از GNU/kFreeBSD افزوده شد.
- threading.enumerate()¶
فهرستی از تمام اشیاء
Threadکه در حال حاضر فعال هستند را برمیگرداند. این فهرست شامل نخهای daemon و اشیاء نخ ساختگی ایجادشده توسطcurrent_thread()است. این فهرست، نخهای خاتمهیافته و نخهایی را که هنوز آغاز نشدهاند مستثنی میکند. با این حال، نخ اصلی همیشه بخشی از نتیجه است، حتی زمانی که خاتمه یافته باشد.
- threading.main_thread()¶
شیء اصلی
Threadرا برمیگرداند. در شرایط عادی، نخ اصلی، نخی است که مفسر پایتون از آن شروع شده است.اضافه شده در نسخهی 3.4.
- threading.settrace(func)¶
یک تابع ردگیری برای همه نخهایی که از ماژول
threadingآغاز میشوند تنظیم کنید. func برای هر نخ، پیش از فراخوانی متدrun()آن، بهsys.settrace()ارسال میشود.
- threading.settrace_all_threads(func)¶
یک تابع ردگیری را برای همه نخهای شروعشده از ماژول
threadingو همه نخهای پایتون که در حال حاضر در حال اجرا هستند، تنظیم کنید.func برای هر نخ، پیش از آنکه متد
run()آن فراخوانی شود، بهsys.settrace()ارسال میشود.اضافه شده در نسخهی 3.12.
- threading.gettrace()¶
تابع ردگیری تنظیمشده توسط
settrace()را دریافت کنید.اضافه شده در نسخهی 3.10.
- threading.setprofile(func)¶
یک تابع پروفایل برای همه نخهای شروعشده از ماژول
threadingتنظیم کنید. func برای هر نخ، پیش از فراخوانی متدrun()آن، بهsys.setprofile()ارسال میشود.
- threading.setprofile_all_threads(func)¶
برای همه نخهای آغازشده از ماژول
threadingو همه نخهای پایتون که هماکنون در حال اجرا هستند، یک تابع پروفایل (profile function) تنظیم کنید.func برای هر نخ به
sys.setprofile()ارسال میشود، پیش از آنکه متدrun()آن فراخوانی شود.اضافه شده در نسخهی 3.12.
- threading.getprofile()¶
تابع پروفایلگیر تنظیمشده توسط
setprofile()را دریافت کنید.اضافه شده در نسخهی 3.10.
- threading.stack_size([size])¶
اندازه پشته نخ مورد استفاده هنگام ایجاد نخهای جدید را بازمیگرداند. آرگومان اختیاری size اندازه پشته مورد استفاده برای نخهای ایجادشده پس از آن را مشخص میکند و باید ۰ (استفاده از پیشفرض پلتفرم یا پیشفرض پیکربندیشده) یا یک مقدار عدد صحیح مثبت حداقل ۳۲٬۷۶۸ (۳۲ KiB) باشد. اگر size مشخص نشده باشد، از ۰ استفاده میشود. اگر تغییر اندازه پشته نخ پشتیبانی نشود،
RuntimeErrorپرتاب میشود. اگر اندازه پشته مشخصشده نامعتبر باشد،ValueErrorپرتاب میشود و اندازه پشته بدون تغییر میماند. ۳۲ KiB در حال حاضر حداقل مقدار پشتیبانیشده برای اندازه پشته است تا فضای پشته کافی برای خود مفسر تضمین شود. توجه داشته باشید که برخی پلتفرمها ممکن است محدودیتهای خاصی برای مقادیر اندازه پشته داشته باشند، مانند الزام به این که حداقل اندازه پشته بزرگتر از ۳۲ KiB باشد یا الزام به تخصیص بهصورت مضربهایی از اندازه صفحه حافظه سیستم؛ برای اطلاعات بیشتر باید به مستندات پلتفرم مراجعه کنید (صفحههای ۴ KiB رایج هستند؛ استفاده از مضربهای ۴۰۹۶ برای اندازه پشته، روش پیشنهادی در نبود اطلاعات خاصتر است).دسترسپذیری: Windows, pthreads.
سکوهای یونیکس با پشتیبانی از نخهای POSIX.
این ماژول همچنین ثابت زیر را تعریف میکند:
- threading.TIMEOUT_MAX¶
حداکثر مقدار مجاز برای پارامتر timeout توابع مسدودکننده (
Lock.acquire()،RLock.acquire()،Condition.wait()و غیره). تعیین timeout بیشتر از این مقدار باعث پرتابOverflowErrorمیشود.اضافه شده در نسخهی 3.2.
این ماژول تعدادی کلاس تعریف میکند که در بخشهای زیر بهتفصیل شرح داده شدهاند.
طراحی این ماژول بهطور تقریبی بر پایهی مدل نخبندی جاوا است. با این حال، در حالی که جاوا قفلها و متغیرهای شرطی را بهعنوان رفتار پایهی هر شیء قرار میدهد، آنها در پایتون اشیای جداگانهای هستند. کلاس Thread پایتون از زیرمجموعهای از رفتار کلاس Thread جاوا پشتیبانی میکند؛ در حال حاضر، هیچ اولویتی و هیچ گروه نخی وجود ندارد و نخها نمیتوانند نابود، متوقف، معلق، از سر گرفته یا قطع شوند. متدهای ایستای کلاس Thread جاوا، در صورت پیادهسازی، به توابع سطح ماژول نگاشت میشوند.
همهی متدهایی که در زیر توضیح داده شدهاند، بهصورت اتمی اجرا میشوند.
دادههای محلی نخ¶
داده محلی به نخ، دادهای است که مقادیر آن مختص نخ است. اگر دادهای دارید که میخواهید محلی به یک نخ باشد، یک شیء local ایجاد کنید و از ویژگیهای آن استفاده کنید:
>>> mydata = local()
>>> mydata.number = 42
>>> mydata.number
42
همچنین میتوانید به دیکشنری شیء local دسترسی پیدا کنید:
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
اگر به دادهها در یک نخ دیگر دسترسی داشته باشیم:
>>> log = []
>>> def f():
... items = sorted(mydata.__dict__.items())
... log.append(items)
... mydata.number = 11
... log.append(mydata.number)
>>> import threading
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[], 11]
ما دادههای متفاوتی دریافت میکنیم. علاوه بر این، تغییرات اعمالشده در نخ دیگر بر دادههای دیدهشده در این نخ تأثیر نمیگذارند:
>>> mydata.number
42
البته، مقدارهایی که از یک شیء local به دست میآورید، از جمله ویژگی __dict__ آنها، مربوط به هر نخی هستند که در زمان خوانده شدن ویژگی جاری بوده است. به همین دلیل، معمولاً نباید این مقدارها را بین نخها ذخیره کنید، زیرا آنها فقط به نخی که از آن آمدهاند اعمال میشوند.
شما میتوانید با زیرکلاسسازی از کلاس local، اشیای سفارشی local ایجاد کنید:
>>> class MyLocal(local):
... number = 2
... def __init__(self, /, **kw):
... self.__dict__.update(kw)
... def squared(self):
... return self.number ** 2
این میتواند برای پشتیبانی از مقادیر پیشفرض، متدها و مقداردهی اولیه مفید باشد. توجه داشته باشید که اگر متد __init__() را تعریف کنید، هر بار که از شیء local در یک نخ جداگانه استفاده شود، فراخوانی میشود. این برای مقداردهی اولیه دیکشنری هر نخ ضروری است.
اکنون اگر یک شیء local ایجاد کنیم:
>>> mydata = MyLocal(color='red')
یک عدد پیشفرض داریم:
>>> mydata.number
2
یک رنگ اولیه:
>>> mydata.color
'red'
>>> del mydata.color
و متدی که روی دادهها عمل میکند:
>>> mydata.squared()
4
مانند قبل، میتوانیم به دادهها در یک نخ جداگانه دسترسی داشته باشیم:
>>> log = []
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[('color', 'red')], 11]
بدون تأثیر بر دادههای این نخ:
>>> mydata.number
2
>>> mydata.color
Traceback (most recent call last):
...
AttributeError: 'MyLocal' object has no attribute 'color'
توجه داشته باشید که زیرکلاسها میتوانند __slots__ را تعریف کنند، اما آنها محلی برای نخ نیستند. آنها میان نخها به اشتراک گذاشته میشوند:
>>> class MyLocal(local):
... __slots__ = 'number'
>>> mydata = MyLocal()
>>> mydata.number = 42
>>> mydata.color = 'red'
بنابراین، نخ جداگانه:
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
بر آنچه میبینیم تأثیر میگذارد:
>>> mydata.number
11
- class threading.local¶
کلاسی که دادههای محلی نخ (thread-local) را نشان میدهد.
اشیای نخ¶
کلاس Thread نشاندهندهی فعالیتی است که در یک نخ کنترلی جداگانه اجرا میشود. دو راه برای مشخص کردن فعالیت وجود دارد: با ارسال یک شیء فراخوانیپذیر به سازنده، یا با بازنویسی متد run() در یک زیرکلاس. هیچ متد دیگری (بهجز سازنده) نباید در یک زیرکلاس بازنویسی شود. به عبارت دیگر، فقط متدهای __init__() و run() این کلاس را بازنویسی کنید.
پس از ایجاد یک شیء نخ، فعالیت آن باید با فراخوانی متد start() نخ آغاز شود. این کار متد run() را در یک نخ کنترلی جداگانه فراخوانی میکند.
هنگامی که فعالیت نخ آغاز میشود، نخ «زنده» در نظر گرفته میشود. هنگامی که متد run() آن پایان مییابد، دیگر زنده نیست — چه بهطور عادی و چه با پرتاب یک استثنای مدیریتنشده. متد is_alive() زنده بودن نخ را بررسی میکند.
نخهای دیگر میتوانند متد join() یک نخ را فراخوانی کنند. این کار نخ فراخواننده را مسدود میکند تا زمانی که نخی که متد join() آن فراخوانی شده است، خاتمه یابد.
هر نخ دارای یک نام است. این نام را میتوان به سازنده ارسال کرد و از طریق ویژگی name خواند یا تغییر داد.
اگر متد run() استثنایی پرتاب کند، برای رسیدگی به آن، threading.excepthook() فراخوانی میشود. بهطور پیشفرض، threading.excepthook() از SystemExit بهصورت بیصدا چشمپوشی میکند.
یک نخ میتواند بهعنوان «نخ daemon (daemon thread)» علامتگذاری شود. اهمیت این پرچم در این است که وقتی فقط نخهای daemon باقی مانده باشند، کل برنامهی Python خارج میشود. مقدار اولیه از نخ ایجادکننده به ارث برده میشود. این پرچم را میتوان از طریق ویژگی daemon یا آرگومان daemon در سازنده تنظیم کرد.
توجه
نخهای daemon بهطور ناگهانی در هنگام خاموش شدن متوقف میشوند. منابع آنها (مانند پروندههای باز، تراکنشهای پایگاه داده، و غیره) ممکن است بهدرستی آزاد نشوند. اگر میخواهید نخهای شما بهآرامی متوقف شوند، آنها را غیر daemon کنید و از یک سازوکار سیگنالدهی مناسب مانند Event استفاده کنید.
یک شیء «نخ اصلی» وجود دارد؛ این شیء متناظر با نخ آغازین کنترل در برنامه پایتون است. این یک نخ daemon نیست.
این احتمال وجود دارد که «اشیاء نخ ساختگی (dummy thread objects)» ایجاد شوند. اینها اشیاء نخ هستند که با «نخهای بیگانه (alien threads)» متناظرند؛ نخهای کنترلی که خارج از ماژول threading، مثلاً مستقیماً از کد C، آغاز شدهاند. اشیاء نخ ساختگی قابلیت محدودی دارند؛ آنها همیشه زنده و daemon (daemonic) در نظر گرفته میشوند و نمیتوان آنها را الحاق کرد. آنها هرگز حذف نمیشوند، زیرا تشخیص پایان نخهای بیگانه غیرممکن است.
- class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None, context=None)¶
این سازنده باید همیشه با آرگومانهای کلیدواژهای فراخوانی شود. آرگومانها عبارتند از:
group باید
Noneباشد، زیرا برای گسترش آینده در زمانی که یک کلاسThreadGroupپیادهسازی شود، رزرو شده است.target شیء فراخوانیپذیری است که توسط متد
run()فراخوانی میشود. مقدار پیشفرض آنNoneاست، یعنی هیچ چیزی فراخوانی نمیشود.name نام نخ است. بهطور پیشفرض، یک نام یکتا در قالب «Thread-N» ساخته میشود که در آن N یک عدد دهدهی کوچک است، یا «Thread-N (target)» که در آن «target» برابر
target.__name__است، در صورتی که آرگومان target مشخص شده باشد.args یک فهرست یا تاپل از آرگومانها برای فراخوانی هدف است. مقدار پیشفرض آن
()است.kwargs یک دیکشنری از آرگومانهای کلیدواژهای برای فراخوانی هدف است. پیشفرض آن
{}است.اگر
Noneنباشد، daemon بهصراحت تعیین میکند که آیا نخ daemon است یا خیر. اگرNoneباشد (پیشفرض)، ویژگی daemon از نخ جاری به ارث میرسد.context مقدار
Contextاست که هنگام آغاز نخ استفاده میشود. مقدار پیشفرضNoneاست، که نشان میدهد پرچمsys.flags.thread_inherit_contextرفتار را کنترل میکند. اگر پرچم true باشد، نخها با یک کپی از زمینهی فراخوانندهیstart()آغاز میشوند. اگر false باشد، آنها با یک زمینهی خالی آغاز میشوند. برای آغاز بهطور صریح با یک زمینهی خالی، یک نمونهی جدید ازContext()را ارسال کنید. برای آغاز بهطور صریح با یک کپی از زمینهی جاری، مقدار حاصل ازcopy_context()را ارسال کنید. مقدار پیشفرض این پرچم در ساختهای نخآزاد برابر true و در غیر این صورت false است.اگر زیرکلاس سازنده را بازنویسی کند، باید پیش از انجام هر کار دیگری روی نخ، از فراخوانی سازندهی کلاس پایه (
Thread.__init__()) اطمینان حاصل کند.تغییر یافته در نسخهی 3.3: پارامتر daemon اضافه شد.
تغییر یافته در نسخهی 3.10: اگر آرگومان name حذف شده باشد، از نام target استفاده کنید.
تغییر یافته در نسخهی 3.14: پارامتر context افزوده شد.
- start()¶
فعالیت نخ را آغاز میکند.
این باید حداکثر یکبار به ازای هر شیء نخ فراخوانی شود. این ترتیبی میدهد که متد
run()آن شیء در یک نخ کنترل جداگانه فراخوانی شود.این متد در صورت فراخوانی بیش از یک بار روی همان شیء نخ،
RuntimeErrorرا پرتاب خواهد کرد.در صورت پشتیبانی، نام نخ سیستمعامل برابر با
threading.Thread.nameتنظیم میشود. ممکن است نام، بسته به محدودیتهای نام نخ سیستمعامل، کوتاه شود.تغییر یافته در نسخهی 3.14: نام نخ سیستمعامل را تنظیم میکند.
- run()¶
متدی که فعالیت نخ را نشان میدهد.
شما میتوانید این متد را در یک زیرکلاس بازنویسی کنید. متد استاندارد
run()شیء فراخوانیپذیری را که به سازندهی شیء بهعنوان آرگومان target ارسالشده است، در صورت وجود، با آرگومانهای جایگاهی و کلیدواژهای که بهترتیب از آرگومانهای args و kwargs گرفته میشوند، فراخوانی میکند.استفاده از فهرست یا تاپل بهعنوان آرگومان args که به
Threadداده میشود، میتواند همان اثر را داشته باشد.مثال:
>>> from threading import Thread >>> t = Thread(target=print, args=[1]) >>> t.run() 1 >>> t = Thread(target=print, args=(1,)) >>> t.run() 1
- join(timeout=None)¶
صبر کنید تا نخ پایان یابد. این عمل نخ فراخوان را مسدود میکند تا زمانی که نخی که متد
join()آن فراخوانی شده است پایان یابد -- چه بهصورت عادی و چه از طریق استثنای مدیریتنشده -- یا تا زمانی که مهلت زمانی اختیاری رخ دهد.هنگامی که آرگومان timeout وجود داشته باشد و
Noneنباشد، باید یک عدد ممیز شناور باشد که مهلت عملیات را بر حسب ثانیه (یا کسری از ثانیه) مشخص میکند. از آنجا کهjoin()همیشهNoneرا برمیگرداند، باید پس ازjoin()،is_alive()را فراخوانی کنید تا تعیین کنید که آیا پایان مهلت رخ داده است — اگر نخ هنوز زنده است، فراخوانیjoin()با پایان مهلت مواجه شده است.هنگامی که آرگومان timeout وجود ندارد یا
Noneاست، عملیات تا زمانی که نخ پایان یابد مسدود میشود.یک نخ را میتوان چندین بار الحاق کرد.
join()در صورتی که تلاشی برای فراخوانی آن روی نخ جاری انجام شود، یکRuntimeErrorپرتاب میکند، زیرا این کار باعث بنبست میشود. فراخوانیjoin()روی یک نخ پیش از آنکه شروع شده باشد نیز خطا است و تلاشها برای این کار همان استثنا را پرتاب میکنند.اگر برای join کردن یک نخ daemon در حال اجرا در مراحل پایانی نهاییسازی پایتون تلاشی صورت گیرد،
join()یکPythonFinalizationErrorپرتاب میکند.تغییر یافته در نسخهی 3.14: ممکن است
PythonFinalizationErrorرا پرتاب کند.
- name¶
رشتهای که فقط برای اهداف شناسایی استفاده میشود. این رشته هیچ معنایی ندارد. ممکن است به چندین نخ نام یکسانی داده شود. نام اولیه توسط سازنده تنظیم میشود.
در برخی پلتفرمها، نام نخ در زمان شروع نخ در سطح سیستمعامل تنظیم میشود تا در مدیران وظیفه قابل مشاهده باشد. این نام ممکن است برای جا شدن در محدودیت خاص سیستم کوتاه شود (برای مثال، ۱۵ بایت در لینوکس یا ۶۳ بایت در macOS).
تغییرات name تنها زمانی در سطح سیستمعامل منعکس میشود که نخ در حال اجرا تغییر نام داده شود. (تنظیم ویژگی name یک نخ دیگر، فقط شیء Thread پایتون را بهروزرسانی میکند.)
- getName()¶
- setName()¶
API منسوخ برای getter/setter مربوط به
name؛ بهجای آن، مستقیماً از آن بهعنوان یک ویژگی استفاده کنید.منسوخ شده از نسخهی 3.10.
- ident¶
«شناسهی نخ» برای این نخ یا
Noneاگر نخ شروع نشده باشد. این یک عدد صحیح غیرصفر است. تابعget_ident()را ببینید. ممکن است شناسههای نخ زمانی که یک نخ به پایان میرسد و نخ دیگری ایجاد میشود، دوباره استفاده شوند. شناسه حتی پس از پایان یافتن نخ نیز در دسترس است.
- native_id¶
شناسه نخ (
TID) این نخ، که توسط سیستمعامل (هسته) اختصاص داده شده است. این یک عدد صحیح غیرمنفی است، یا اگر نخ شروع نشده باشد،Noneاست. تابعget_native_id()را ببینید. میتوان از این مقدار برای شناسایی یکتای این نخ خاص در سراسر سیستم استفاده کرد (تا زمانی که نخ خاتمه یابد، پس از آن ممکن است این مقدار توسط سیستمعامل بازیافت شود).توجه
مانند شناسههای فرایند، شناسههای نخ تنها از زمان ایجاد نخ تا زمان پایان یافتن آن معتبر هستند (یکتایی آنها در سراسر سیستم تضمینشده است).
دسترسپذیری: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD.
اضافه شده در نسخهی 3.8.
- is_alive()¶
زنده بودن نخ را برمیگرداند.
این متد
Trueرا از درست پیش از آغاز متدrun()تا درست پس از پایان متدrun()بازمیگرداند. تابع ماژولenumerate()فهرستی از همه نخهای زنده را بازمیگرداند.
- daemon¶
یک مقدار بولی که نشان میدهد این نخ یک نخ daemon است (
True) یا خیر (False). این مقدار باید پیش از فراخوانیstart()تنظیم شود، در غیر این صورتRuntimeErrorپرتاب میشود. مقدار اولیه آن از نخ ایجادکننده به ارث میرسد؛ نخ اصلی یک نخ daemon نیست و بنابراین همه نخهایی که در نخ اصلی ایجاد میشوند، بهطور پیشفرضdaemon=Falseهستند.کل برنامه پایتون هنگامی خارج میشود که دیگر هیچ نخ زنده غیر daemon (non-daemon) باقی نمانده باشد.
اشیای قفل¶
یک قفل اولیه، یک سازوکار همگامسازی است که هنگام قفل بودن، متعلق به نخ خاصی نیست. در پایتون، این قفل در حال حاضر پایینترین سطح سازوکار همگامسازی در دسترس است که مستقیماً توسط ماژول توسعهی _thread پیادهسازی شده است.
یک قفل اولیه در یکی از دو حالت «قفلشده» یا «باز» قرار دارد. این قفل در حالت باز ایجاد میشود. دو متد پایه دارد: acquire() و release(). هنگامی که حالت باز است، acquire() حالت را به قفلشده تغییر میدهد و بلافاصله بازمیگردد. هنگامی که حالت قفلشده است، acquire() مسدود میشود تا زمانی که فراخوانی release() در نخی دیگر آن را به حالت باز تغییر دهد، سپس فراخوانی acquire() آن را دوباره به حالت قفلشده تنظیم میکند و بازمیگردد. متد release() باید فقط در حالت قفلشده فراخوانی شود؛ این متد حالت را به باز تغییر میدهد و بلافاصله بازمیگردد. اگر تلاشی برای آزاد کردن یک قفل باز انجام شود، یک RuntimeError پرتاب خواهد شد.
قفلها همچنین از پروتکل مدیریت زمینه پشتیبانی میکنند.
هنگامی که بیش از یک نخ در acquire() مسدود شدهاند و در انتظار هستند تا وضعیت به حالت قفلنشده تغییر کند، هرگاه یک فراخوانی release() وضعیت را به حالت قفلنشده بازنشانی کند، تنها یک نخ ادامه میدهد؛ اینکه کدامیک از نخهای منتظر ادامه میدهد تعریف نشده است و ممکن است در پیادهسازیهای مختلف متفاوت باشد.
همهی متدها بهصورت اتمی اجرا میشوند.
- class threading.Lock¶
کلاسی که اشیای قفل اولیه را پیادهسازی میکند. پس از آنکه یک نخ قفلی را کسب کرد، تلاشهای بعدی برای کسب آن مسدود میشوند، تا زمانی که آزاد شود؛ هر نخی میتواند آن را آزاد کند.
تغییر یافته در نسخهی 3.13:
Lockاکنون یک کلاس است. در پایتونهای پیشین،Lockیک تابع کارخانه (factory function) بود که نمونهای از نوع قفل خصوصی زیربنایی را برمیگرداند.- acquire(blocking=True, timeout=-1)¶
یک قفل را بهصورت مسدودکننده یا غیرمسدودکننده کسب کنید.
هنگامی که با آرگومان blocking تنظیمشده روی
True(پیشفرض) فراخوانی شود، تا آزاد شدن قفل مسدود میشود، سپس آن را در حالت قفلشده قرار میدهد وTrueرا برمیگرداند.هنگامی که با آرگومان blocking تنظیمشده روی
Falseفراخوانی شود، مسدود نمیکند. اگر فراخوانی با blocking تنظیمشده رویTrueقرار باشد مسدود شود، بلافاصلهFalseرا برمیگرداند؛ در غیر این صورت، قفل را در وضعیت قفلشده قرار میدهد وTrueرا برمیگرداند.هنگام فراخوانی با آرگومان timeout از نوع ممیز شناور که روی مقدار مثبت تنظیم شده باشد، حداکثر به تعداد ثانیههای مشخصشده توسط timeout و تا زمانی که قفل قابل کسب نباشد، مسدود میشود. آرگومان timeout با مقدار
-1یک انتظار نامحدود را مشخص میکند. تعیین timeout زمانی که blocking برابرFalseباشد ممنوع است.مقدار بازگشتی
Trueاست اگر قفل با موفقیت گرفته شود، در غیر این صورتFalseاست (برای مثال اگر timeout منقضی شده باشد).تغییر یافته در نسخهی 3.2: پارامتر timeout جدید است.
تغییر یافته در نسخهی 3.2: اکنون میتوان تهیه قفل را در POSIX با سیگنالها قطع کرد، در صورتی که پیادهسازی زیربنایی نخبندی (threading) از این قابلیت پشتیبانی کند.
تغییر یافته در نسخهی 3.14: اکنون میتوان کسب قفل را در ویندوز با سیگنالها قطع کرد.
- release()¶
آزاد کردن یک قفل. میتوان آن را از هر نخی فراخوانی کرد، نه فقط از نخی که قفل را کسب کرده است.
هنگامی که قفل، قفل است، آن را به حالت باز بازنشانی کنید و بازگردید. اگر نخهای دیگری مسدود شدهاند و در انتظار باز شدن قفل هستند، دقیقاً به یکی از آنها اجازهی ادامه بدهید.
هنگام فراخوانی روی یک قفل باز، یک
RuntimeErrorپرتاب میشود.هیچ مقدار بازگشتی وجود ندارد.
- locked()¶
اگر قفل بهدست آمده باشد،
Trueبرمیگرداند.
اشیای RLock¶
قفل بازورودپذیر (reentrant lock) یک سازوکار اولیهی همگامسازی است که یک نخ یکسان میتواند آن را چندین بار کسب کند. از نظر داخلی، این قفل علاوه بر وضعیت قفلشده/قفلنشدهی استفادهشده در قفلهای اولیه، از مفاهیم «نخ مالک» و «سطح بازگشت» استفاده میکند. در وضعیت قفلشده، نخی مالک قفل است؛ در وضعیت قفلنشده، هیچ نخی مالک آن نیست.
نخها برای قفل کردن یک قفل، متد acquire() آن و برای باز کردن قفل آن، متد release() آن را فراخوانی میکنند.
توجه
قفلهای بازورودپذیر از پروتکل مدیریت زمینه پشتیبانی میکنند، بنابراین توصیه میشود بهجای فراخوانی دستی acquire() و release() برای مدیریت کسب و آزادسازی قفل در یک بلوک کد، از with استفاده کنید.
جفتهای فراخوانی acquire()/release() در RLock میتوانند تودرتو باشند، برخلاف acquire()/release() در Lock. تنها آخرین release() (یعنی release() از بیرونیترین جفت) قفل را به وضعیت قفلنشده بازنشانی میکند و به نخ دیگری که در acquire() مسدود شده است اجازه میدهد ادامه دهد.
acquire()/release() باید بهصورت جفت استفاده شوند: هر فراخوانی acquire باید یک فراخوانی release در نخی که قفل را کسب کرده است داشته باشد. عدم فراخوانی release به تعداد دفعاتی که قفل کسب شده است میتواند منجر به بنبست شود.
- class threading.RLock¶
این کلاس اشیای قفل بازورودپذیر (reentrant lock) را پیادهسازی میکند. یک قفل بازورودپذیر باید توسط نخی که آن را به دست آورده است آزاد شود. هنگامی که نخی یک قفل بازورودپذیر را به دست آورده باشد، همان نخ میتواند دوباره آن را بدون مسدود شدن به دست آورد؛ نخ باید به ازای هر باری که آن را به دست آورده است، یکبار آن را آزاد کند.
توجه داشته باشید که
RLockدر واقع یک تابع کارخانهای است که نمونهای از کارآمدترین نسخهی کلاس ملموس RLock را برمیگرداند که توسط پلتفرم پشتیبانی میشود.- acquire(blocking=True, timeout=-1)¶
یک قفل را بهصورت مسدودکننده یا غیرمسدودکننده کسب کنید.
همچنین ملاحظه نمائید
- استفاده از RLock بهعنوان مدیر زمینه
هر زمان که عملی باشد، به جای فراخوانیهای دستی
acquire()وrelease()توصیه میشود.
هنگامی که با آرگومان blocking روی
True(پیشفرض) فراخوانی شود:اگر هیچ نخی مالک قفل نباشد، قفل را به دست میآورد و بلافاصله بازمیگردد.
اگر نخ دیگری مالک قفل باشد، مسدود میشود تا زمانی که بتوانیم قفل را کسب کنیم، یا تا timeout، اگر روی یک مقدار float مثبت تنظیم شده باشد.
اگر همان نخ مالک قفل باشد، قفل را دوباره به دست میآورد و بلافاصله بازمیگردد. این تفاوت میان
LockوRLockاست؛Lockاین حالت را مانند حالت قبلی مدیریت میکند و تا زمانی که قفل بتواند به دست آید، مسدود میشود.
هنگامی که با آرگومان blocking تنظیمشده روی
Falseفراخوانی شود:اگر هیچ نخی مالک قفل نباشد، قفل را به دست میآورد و بلافاصله بازمیگردد.
اگر نخ دیگری مالک قفل باشد، بلافاصله بازمیگردد.
اگر همان نخ مالک قفل باشد، قفل را دوباره به دست میآورد و بلافاصله بازمیگردد.
در همهی موارد، اگر نخ توانسته باشد قفل را به دست آورد،
Trueبرمیگرداند. اگر نخ نتوانسته باشد قفل را به دست آورد (یعنی اگر مسدودکننده نباشد یا مهلت به پایان رسیده باشد)،Falseبرمیگرداند.اگر چندین بار فراخوانی شود، فراخوانی نکردن
release()به همان تعداد ممکن است به بنبست منجر شود. بهتر است بهجای فراخوانی مستقیم acquire/release، ازRLockبهعنوان مدیر زمینه استفاده کنید.تغییر یافته در نسخهی 3.2: پارامتر timeout جدید است.
- release()¶
یک قفل را آزاد میکند و سطح بازگشت را کاهش میدهد. اگر پس از کاهش، سطح بازگشت صفر باشد، قفل به حالت باز (قفلنشده) بازنشانی میشود (متعلق به هیچ نخی نیست)، و اگر نخهای دیگری در انتظار باز شدن قفل مسدود شده باشند، دقیقاً به یکی از آنها اجازهی ادامه داده میشود. اگر پس از کاهش، سطح بازگشت هنوز ناصفر باشد، قفل همچنان قفلشده و متعلق به نخ فراخوانیکننده باقی میماند.
این متد را تنها زمانی فراخوانی کنید که نخ فراخواننده مالک قفل است. اگر این متد در زمانی که قفل گرفته نشده است فراخوانی شود، یک
RuntimeErrorپرتاب میشود.هیچ مقدار بازگشتی وجود ندارد.
- locked()¶
یک بولی برمیگرداند که نشان میدهد آیا این شیء در حال حاضر قفل است یا خیر.
اضافه شده در نسخهی 3.14.
اشیای شرط¶
یک متغیر شرط همیشه با نوعی قفل همراه است؛ میتوان این قفل را بهعنوان آرگومان ارسال کرد یا بهطور پیشفرض یکی ایجاد میشود. ارسال آن زمانی مفید است که چندین متغیر شرط باید یک قفل مشترک داشته باشند. قفل بخشی از شیء شرط است: نیازی نیست آن را بهطور جداگانه پیگیری کنید.
یک متغیر شرط از پروتکل مدیریت زمینه پیروی میکند: استفاده از دستور with، قفل مرتبط را در طول بلوک محصورشده به دست میآورد. متدهای acquire() و release() نیز متدهای متناظر قفل مرتبط را فراخوانی میکنند.
سایر متدها باید در حالی فراخوانی شوند که قفل مرتبط نگه داشته شده باشد. متد wait() قفل را آزاد میکند و سپس مسدود میشود تا نخ دیگری با فراخوانی notify() یا notify_all() آن را بیدار کند. پس از بیدار شدن، wait() دوباره قفل را به دست میآورد و بازمیگردد. همچنین میتوان یک مهلت زمانی مشخص کرد.
متد notify() یکی از نخهای در انتظار متغیر شرطی را، در صورتی که منتظری وجود داشته باشد، بیدار میکند. متد notify_all() همه نخهای در انتظار متغیر شرطی را بیدار میکند.
توجه: متدهای notify() و notify_all() قفل را آزاد نمیکنند؛ این بدان معناست که نخ یا نخهای بیدارشده بلافاصله از فراخوانی wait() خود بازنمیگردند، بلکه تنها زمانی بازمیگردند که نخی که notify() یا notify_all() را فراخوانی کرده باشد، سرانجام مالکیت قفل را واگذار کند.
سبک برنامهنویسی رایج با استفاده از متغیرهای شرطی، از قفل برای همگامسازی دسترسی به یک وضعیت مشترک استفاده میکند؛ نخهایی که به تغییر خاصی در وضعیت علاقهمند هستند، wait() را بهطور مکرر فراخوانی میکنند تا وضعیت مطلوب را مشاهده کنند، در حالی که نخهایی که وضعیت را تغییر میدهند، هنگامی که وضعیت را بهگونهای تغییر میدهند که ممکن است وضعیت مطلوبی برای یکی از نخهای منتظر باشد، notify() یا notify_all() را فراخوانی میکنند. برای مثال، کد زیر یک موقعیت عام تولیدکننده-مصرفکننده با ظرفیت نامحدود بافر است:
# Consume one item
with cv:
while not an_item_is_available():
cv.wait()
get_an_available_item()
# Produce one item
with cv:
make_an_item_available()
cv.notify()
حلقهی while که شرط برنامه را بررسی میکند، ضروری است، زیرا wait() میتواند پس از مدت زمانی دلخواه و طولانی بازگشت کند، و ممکن است شرطی که موجب فراخوانی notify() شده است دیگر برقرار نباشد. این موضوع ذاتی برنامهنویسی چندنخی است. متد wait_for() میتواند برای خودکارسازی بررسی شرط به کار رود و محاسبهی مهلتهای زمانی را آسانتر کند:
# Consume an item
with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
برای انتخاب بین notify() و notify_all()، در نظر بگیرید که آیا یک تغییر وضعیت میتواند تنها برای یک یا چند نخ در انتظار مهم باشد. بهعنوان مثال در یک موقعیت معمول تولیدکننده-مصرفکننده، افزودن یک آیتم به بافر تنها نیاز به بیدار کردن یک نخ مصرفکننده دارد.
- class threading.Condition(lock=None)¶
این کلاس اشیای متغیر شرطی را پیادهسازی میکند. یک متغیر شرطی به یک یا چند نخ اجازه میدهد تا زمانی که از سوی نخ دیگری مطلع شوند، منتظر بمانند.
اگر آرگومان lock داده شده باشد و
Noneنباشد، باید یک شیءLockیاRLockباشد و بهعنوان قفل زیربنایی استفاده میشود. در غیر این صورت، یک شیءRLockجدید ایجاد میشود و بهعنوان قفل زیربنایی استفاده میشود.تغییر یافته در نسخهی 3.3: از یک تابع کارخانه به یک کلاس تغییر کرده است.
- acquire(*args)¶
قفل زیربنایی را کسب میکند. این متد، متد متناظر را روی قفل زیربنایی فراخوانی میکند؛ مقدار بازگشتی، هر مقداری است که آن متد برمیگرداند.
- release()¶
قفل زیربنایی را آزاد میکند. این متد، متد متناظر روی قفل زیربنایی را فراخوانی میکند؛ مقدار بازگشتی وجود ندارد.
- locked()¶
یک بولی برمیگرداند که نشان میدهد آیا این شیء در حال حاضر قفل است یا خیر.
اضافه شده در نسخهی 3.14.
- wait(timeout=None)¶
تا زمانی که اطلاع داده شود یا مهلت زمانی رخ دهد، منتظر بمانید. اگر نخ فراخوان هنگام فراخوانی این متد قفل را کسب نکرده باشد،
RuntimeErrorپرتاب میشود.این متد قفل زیربنایی را آزاد میکند و سپس مسدود میشود تا با فراخوانی
notify()یاnotify_all()برای همان متغیر شرطی در یک نخ دیگر بیدار شود، یا تا زمانی که مهلت اختیاری به پایان برسد. پس از بیدار شدن یا به پایان رسیدن مهلت، قفل را دوباره به دست میآورد و بازمیگردد.هنگامی که آرگومان timeout وجود دارد و
Noneنیست، باید یک عدد ممیز شناور باشد که مهلت زمانیای را برای عملیات بر حسب ثانیه (یا کسرهایی از آن) مشخص میکند.هنگامی که قفل زیربنایی یک
RLockباشد، با استفاده از متدrelease()آن آزاد نمیشود، زیرا اگر قفل چندین بار بهصورت بازگشتی کسب شده باشد، این کار ممکن است واقعاً قفل را باز نکند. در عوض، از یک رابط داخلی کلاسRLockاستفاده میشود که حتی اگر قفل چندین بار بهصورت بازگشتی کسب شده باشد، واقعاً آن را باز میکند. سپس از یک رابط داخلی دیگر برای بازگرداندن سطح بازگشت، هنگامی که قفل دوباره کسب میشود، استفاده میشود.مقدار بازگشتی
Trueاست، مگر اینکه timeout دادهشده منقضیشده باشد، که در این صورتFalseاست.تغییر یافته در نسخهی 3.2: پیش از این، این متد همیشه
Noneرا برمیگرداند.
- wait_for(predicate, timeout=None)¶
تا زمانی که یک شرط به مقدار درست ارزیابی شود، منتظر بمانید. predicate باید یک فراخوانیپذیر باشد که نتیجهی آن بهعنوان یک مقدار بولی تفسیر میشود. میتوان یک timeout برای تعیین حداکثر مهلت زمانی ارائه کرد.
این متد کاربردی ممکن است
wait()را بهطور مکرر فراخوانی کند تا محمول برقرار شود یا مهلت زمانیای رخ دهد. مقدار بازگشتی، آخرین مقدار بازگشتی محمول است و اگر متد دچار مهلت زمانی شده باشد، بهFalseارزیابی میشود.با چشمپوشی از قابلیت مهلت زمانی، فراخوانی این متد تقریباً معادل نوشتن این است:
while not predicate(): cv.wait()
بنابراین، همان قواعدی که برای
wait()اعمال میشوند، در اینجا نیز اعمال میشوند: هنگام فراخوانی باید قفل گرفتهشده باشد و در زمان بازگشت دوباره گرفته میشود. محمول در حالی که قفل گرفتهشده است، ارزیابی میشود.اضافه شده در نسخهی 3.2.
- notify(n=1)¶
بهطور پیشفرض، در صورت وجود، یک نخ منتظر روی این شرط را بیدار میکند. اگر نخ فراخواننده هنگام فراخوانی این متد قفل را کسب نکرده باشد، یک
RuntimeErrorپرتاب میشود.این متد حداکثر n نخ از نخهایی که در انتظار متغیر شرطی هستند را بیدار میکند؛ اگر هیچ نخی در انتظار نباشد، هیچ کاری انجام نمیشود.
پیادهسازی فعلی، اگر حداقل n نخ در انتظار باشند، دقیقاً n نخ را بیدار میکند. با این حال، اتکا به این رفتار ایمن نیست. ممکن است یک پیادهسازی بهینهشده در آینده گاهی بیش از n نخ را بیدار کند.
توجه: یک نخ بیدارشده در واقع تا زمانی که بتواند قفل را دوباره به دست آورد، از فراخوانی
wait()خود بازگشت نمیکند. از آنجا کهnotify()قفل را آزاد نمیکند، فراخوانندهی آن باید این کار را انجام دهد.
- notify_all()¶
همه نخهای در انتظار این شرط را بیدار میکند. این متد مانند
notify()عمل میکند، اما به جای یک نخ، همه نخهای در انتظار را بیدار میکند. اگر نخ فراخوان هنگام فراخوانی این متد قفل را به دست نیاورده باشد، یکRuntimeErrorپرتاب میشود.متد
notifyAllیک نام مستعار منسوخ برای این متد است.
اشیای سمافور¶
این یکی از قدیمیترین سازوکارهای اولیهی همگامسازی در تاریخ علوم کامپیوتر است که توسط Edsger W. Dijkstra، دانشمند پیشگام هلندی در علوم کامپیوتر، ابداع شد (او بهجای acquire() و release() از نامهای P() و V() استفاده میکرد).
سمافور یک شمارنده داخلی را مدیریت میکند که با هر فراخوانی acquire() کاهش مییابد و با هر فراخوانی release() افزایش مییابد. شمارنده هرگز نمیتواند به زیر صفر برسد؛ هرگاه acquire() مقدار آن را صفر بیابد، مسدود میشود و منتظر میماند تا نخ دیگری release() را فراخوانی کند.
سمافورها همچنین از پروتکل مدیریت زمینه پشتیبانی میکنند.
- class threading.Semaphore(value=1)¶
این کلاس اشیای سمافور را پیادهسازی میکند. یک سمافور یک شمارنده اتمی را مدیریت میکند که بیانگر تعداد فراخوانیهای
release()منهای تعداد فراخوانیهایacquire()، بهعلاوه یک مقدار اولیه است. متدacquire()در صورت لزوم مسدود میشود تا بتواند بدون منفی کردن شمارنده بازگشت کند. اگر داده نشود، value بهطور پیشفرض ۱ است.آرگومان اختیاری، value اولیه برای شمارنده داخلی را مشخص میکند؛ پیشفرض آن
1است. اگر مقدار دادهشده کمتر از ۰ باشد،ValueErrorپرتاب میشود.تغییر یافته در نسخهی 3.3: از یک تابع کارخانه به یک کلاس تغییر کرده است.
- acquire(blocking=True, timeout=None)¶
یک سمافور را کسب میکند.
هنگام فراخوانی بدون آرگومان:
اگر شمارندهی داخلی در هنگام ورود بزرگتر از صفر باشد، آن را یک واحد کاهش دهید و بلافاصله
Trueرا برگردانید.اگر شمارندهی داخلی در زمان ورود صفر باشد، تا زمانی که با فراخوانی
release()بیدار شود، مسدود میشود. پس از بیدار شدن (و زمانی که شمارنده بزرگتر از ۰ باشد)، شمارنده را یک واحد کاهش میدهد وTrueرا برمیگرداند. با هر فراخوانیrelease()، دقیقاً یک نخ بیدار خواهد شد. نباید به ترتیبی که نخها بیدار میشوند، اتکا کرد.
هنگامی که با blocking تنظیمشده روی
Falseفراخوانی شود، مسدود نمیشود. اگر یک فراخوانی بدون آرگومان قرار باشد مسدود شود، بلافاصلهFalseرا برمیگرداند؛ در غیر این صورت، همان کاری را انجام میدهد که هنگام فراخوانی بدون آرگومانها انجام میشود، وTrueرا برمیگرداند.هنگامی که با timeout غیر از
Noneفراخوانی شود، حداکثر به مدت timeout ثانیه مسدود میشود. اگر acquire در این بازه با موفقیت به پایان نرسد،Falseرا برمیگرداند. در غیر این صورت،Trueرا برمیگرداند.تغییر یافته در نسخهی 3.2: پارامتر timeout جدید است.
- release(n=1)¶
یک سمافور را آزاد میکند و شمارنده داخلی را به اندازه n افزایش میدهد. اگر در زمان ورود ۰ بوده باشد و نخهای دیگری در انتظار باشند تا دوباره بزرگتر از ۰ شود، n نخ از آن نخها را بیدار میکند.
تغییر یافته در نسخهی 3.9: پارامتر n اضافه شد تا چندین نخ در انتظار بهصورت همزمان آزاد شوند.
- class threading.BoundedSemaphore(value=1)¶
کلاسی که اشیای سمافور محدود (bounded semaphore) را پیادهسازی میکند. سمافور محدود بررسی میکند که مقدار فعلیاش از مقدار اولیهاش بیشتر نشود. در این صورت،
ValueErrorپرتاب میشود. در بیشتر موارد، از سمافورها برای محافظت از منابع با ظرفیت محدود استفاده میشود. اگر سمافور بیش از حد آزاد شود، نشانهای از یک اشکال است. اگر داده نشود، value بهطور پیشفرض ۱ است.تغییر یافته در نسخهی 3.3: از یک تابع کارخانه به یک کلاس تغییر کرده است.
مثال Semaphore¶
از سمافورها اغلب برای محافظت از منابعی با ظرفیت محدود استفاده میشود، برای مثال، یک سرور پایگاهداده. در هر وضعیتی که اندازهی منبع ثابت است، باید از سمافور محدود (bounded semaphore) استفاده کنید. پیش از ایجاد هر نخ کارگری، نخ اصلی شما سمافور را مقداردهی اولیه میکند:
maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)
پس از ایجاد، نخهای کارگر هنگامی که نیاز به اتصال به سرور دارند، متدهای acquire و release سمافور را فراخوانی میکنند:
with pool_sema:
conn = connectdb()
try:
# ... use connection ...
finally:
conn.close()
استفاده از یک سمافور محدود (bounded semaphore) احتمال بدون تشخیص ماندن یک خطای برنامهنویسی را، که باعث میشود سمافور بیش از تعداد دفعات کسبشده آزاد شود، کاهش میدهد.
اشیای رویداد¶
این یکی از سادهترین سازوکارها برای ارتباط میان نخها است: یک نخ رویدادی را سیگنال میکند و نخهای دیگر منتظر آن میمانند.
یک شیء رویداد، یک پرچم داخلی را مدیریت میکند که میتوان آن را با متد set() روی true تنظیم کرد و با متد clear() به false بازنشانی کرد. متد wait() تا زمانی که پرچم true شود، مسدود میشود.
- class threading.Event¶
کلاسی که اشیای رویداد را پیادهسازی میکند. یک رویداد پرچمی را مدیریت میکند که میتوان آن را با متد
set()روی درست تنظیم کرد و با متدclear()به نادرست بازنشانی کرد. متدwait()تا زمانی که پرچم درست شود، مسدود میشود. پرچم در ابتدا نادرست است.تغییر یافته در نسخهی 3.3: از یک تابع کارخانه به یک کلاس تغییر کرده است.
- is_set()¶
فقط و فقط در صورتی
Trueرا برمیگرداند که پرچم داخلی true باشد.متد
isSetیک نام مستعار منسوخ برای این متد است.
- set()¶
پرچم داخلی را روی true قرار دهید. همه نخهایی که منتظر true شدن آن هستند، بیدار میشوند. نخهایی که پس از true بودن پرچم،
wait()را فراخوانی کنند، بههیچوجه مسدود نخواهند شد.
- clear()¶
پرچم داخلی را روی false بازنشانی میکند. پس از آن، نخهایی که
wait()را فراخوانی میکنند، مسدود میشوند تا زمانی کهset()فراخوانی شود و پرچم داخلی دوباره روی true تنظیم شود.
- wait(timeout=None)¶
تا زمانی که پرچم داخلی نادرست است و مهلت، در صورت تعیین شدن، منقضی نشده باشد، مسدود میکند. مقدار بازگشتی، دلیل بازگشت این متد مسدودکننده را نشان میدهد؛
Trueاگر بازگشت به این دلیل باشد که پرچم داخلی روی درست تنظیم شده است، یاFalseاگر مهلتی تعیین شده باشد و پرچم داخلی در مهلت زمانی تعیینشده درست نشده باشد.هنگامی که آرگومان timeout وجود دارد و
Noneنیست، باید یک عدد اعشاری باشد که مهلت زمانی عملیات را بر حسب ثانیه یا کسری از ثانیه مشخص میکند.تغییر یافته در نسخهی 3.1: پیش از این، این متد همیشه
Noneرا برمیگرداند.
اشیای Timer¶
این کلاس نشاندهندهی عملیاتی است که باید تنها پس از گذشت مدت زمان معینی اجرا شود — یک زمانسنج. Timer یک زیرکلاس از Thread است و از این رو، بهعنوان نمونهای از ایجاد نخهای سفارشی نیز عمل میکند.
زمانسنجها، مانند نخها، با فراخوانی متد Timer.start خود آغاز میشوند. میتوان زمانسنج را (پیش از آغاز عملش) با فراخوانی متد cancel() متوقف کرد. ممکن است بازه زمانی که زمانسنج پیش از اجرای عمل خود منتظر میماند، دقیقاً برابر با بازه زمانی که کاربر مشخص کرده است نباشد.
برای مثال:
def hello():
print("hello, world")
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
- class threading.Timer(interval, function, args=None, kwargs=None)¶
یک زمانسنج ایجاد کنید که function را با آرگومانهای args و آرگومانهای کلیدواژهای kwargs، پس از گذشت interval ثانیه اجرا خواهد کرد. اگر args برابر
Noneباشد (پیشفرض)، از یک فهرست خالی استفاده خواهد شد. اگر kwargs برابرNoneباشد (پیشفرض)، از یک دیکشنری خالی استفاده خواهد شد.تغییر یافته در نسخهی 3.3: از یک تابع کارخانه به یک کلاس تغییر کرده است.
- cancel()¶
تایمر را متوقف کنید و اجرای عمل تایمر را لغو کنید. این کار تنها زمانی کار میکند که تایمر هنوز در مرحلهی انتظار باشد.
اشیای سد (Barrier)¶
اضافه شده در نسخهی 3.2.
این کلاس یک سازوکار همگامسازی ساده برای استفادهی تعداد ثابتی از نخها فراهم میکند که نیاز دارند منتظر یکدیگر بمانند. هر یک از نخها تلاش میکند با فراخوانی متد wait() از سد عبور کند و تا زمانی که همهی نخها فراخوانیهای wait() خود را انجام ندهند، مسدود میشود. در این نقطه، نخها بهطور همزمان آزاد میشوند.
میتوان از این سد برای همان تعداد نخ، به هر تعداد بار دوباره استفاده کرد.
بهعنوان مثال، در اینجا یک روش ساده برای همگامسازی نخ کلاینت و سرور آمده است:
b = Barrier(2, timeout=5)
def server():
start_server()
b.wait()
while True:
connection = accept_connection()
process_server_connection(connection)
def client():
b.wait()
while True:
connection = make_connection()
process_client_connection(connection)
- class threading.Barrier(parties, action=None, timeout=None)¶
یک شیء سد (barrier) برای تعداد parties نخ ایجاد کنید. اگر action ارائه شود، یک شیء فراخوانیپذیر است که هنگامی که نخها آزاد میشوند، توسط یکی از آنها فراخوانی میشود. timeout مقدار پیشفرض مهلت است، اگر هیچ مقداری برای متد
wait()مشخص نشده باشد.- wait(timeout=None)¶
از سد عبور میکند. هنگامی که همه نخهای شرکتکننده در سد این تابع را فراخوانی کرده باشند، همه آنها همزمان آزاد میشوند. اگر timeout ارائه شده باشد، از این مقدار به جای هر مقداری که به سازنده کلاس شده باشد استفاده میشود.
مقدار بازگشتی یک عدد صحیح در بازهی ۰ تا parties -- ۱ است و برای هر نخ متفاوت است. میتوان از این مقدار برای انتخاب یک نخ جهت انجام برخی کارهای نظافتی خاص استفاده کرد، مثلاً:
i = barrier.wait() if i == 0: # Only one thread needs to print this print("passed the barrier")
اگر action به سازنده ارائه شده باشد، یکی از نخها آن را پیش از آزاد شدن فراخوانی خواهد کرد. چنانچه این فراخوانی خطایی را پرتاب کند، سد (barrier) در وضعیت شکسته قرار میگیرد.
اگر مهلت فراخوانی به پایان برسد، سد در حالت شکسته قرار میگیرد.
این متد ممکن است در صورتی که سد (barrier) در حین انتظار یک نخ شکسته یا بازنشانی شود، استثنای
BrokenBarrierErrorرا پرتاب کند.
- reset()¶
سد (barrier) را به وضعیت پیشفرض و خالی بازمیگرداند. هر نخی که در انتظار آن باشد، استثنای
BrokenBarrierErrorرا دریافت خواهد کرد.توجه داشته باشید که استفاده از این تابع ممکن است در صورت وجود نخهای دیگری که وضعیت آنها نامشخص است، به کمی همگامسازی خارجی نیاز داشته باشد. اگر یک سد (barrier) شکسته شود، ممکن است بهتر باشد صرفاً آن را رها کنید و یک سد جدید ایجاد کنید.
- abort()¶
سد را در وضعیت شکسته قرار دهید. این کار باعث میشود هر فراخوانی فعال یا آیندهی
wait()باBrokenBarrierErrorشکست بخورد. برای مثال، اگر یکی از نخها نیاز به لغو شدن دارد، از این کار استفاده کنید تا از بنبست برنامه جلوگیری شود.ممکن است بهتر باشد صرفاً یک سد (barrier) با مقدار معقول timeout ایجاد کنید تا بهطور خودکار در برابر از مسیر خارج شدن یکی از نخها محافظت شود.
- parties¶
تعداد نخهای لازم برای عبور از سد.
- n_waiting¶
تعداد نخهایی که در حال حاضر در سد منتظر هستند.
- broken¶
یک بولی که اگر سد در وضعیت شکسته باشد،
Trueاست.
- exception threading.BrokenBarrierError¶
این استثنا، که زیرکلاسی از
RuntimeErrorاست، زمانی پرتاب میشود که شیءBarrierبازنشانی یا شکسته شود.
استفاده از قفلها، شرایط و سمافورها در دستور with¶
همهی اشیاء ارائهشده توسط این ماژول که متدهای acquire و release دارند، میتوانند بهعنوان مدیر زمینه برای دستور with استفاده شوند. متد acquire هنگام ورود به بلوک فراخوانی میشود و release هنگام خروج از بلوک فراخوانی خواهد شد. بنابراین، قطعهکد زیر:
with some_lock:
# do something...
معادل است با:
some_lock.acquire()
try:
# do something...
finally:
some_lock.release()
در حال حاضر، میتوان از اشیای Lock، RLock، Condition، Semaphore و BoundedSemaphore بهعنوان مدیران زمینهی دستور with استفاده کرد.