آموزش ماژول threading – ویژگی daemon

Please login to bookmark Close

ویژگی daemon

گاهی می‌خواهیم یک ترد در «پس‌زمینه» اجرا شود؛ یعنی کارش را انجام دهد اما مانع پایان‌یافتن برنامه نشود. برای این کار می‌توان ترد را به‌صورت daemon تعریف کرد. تردها به‌صورت پیش‌فرض daemon نیستند.

تفاوت کلیدی در «پایان زندگی» است: برنامه‌ی پایتون تا زمانی که حتی یک تردِ غیرdaemon در حال اجرا باشد، زنده می‌ماند و منتظر آن می‌ماند. اما تردهای daemon این‌طور نیستند؛ به‌محض اینکه همه‌ی تردهای غیرdaemon (از جمله ترد اصلی) تمام شوند، تردهای daemon به‌صورت خودکار و بدون تکمیل کارشان بسته می‌شوند. به‌همین دلیل daemon برای «سناریوهای فرعی» مناسب است.

سناریوی فرعی، کاری است که حذف‌شدنش خللی در هدف اصلی برنامه ایجاد نمی‌کند و برنامه همچنان خروجی درست می‌دهد. چند نمونه:

  • مانیتورینگ منابع (مثل مصرف حافظه یا CPU) هنگام اجرا
  • ارسال گزارش (log) هنگام اجرا
  • ارسال اعلان‌ها یا هشدارها هنگام اجرا
  • پاک‌سازی حافظه یا کش هنگام اجرا
  • ذخیره‌سازی یا پشتیبان‌گیری خودکار هنگام اجرا

مثال: اطلاع‌رسانی به کاربر

برنامه‌ای داریم که اجرایش زمان قابل‌توجهی طول می‌کشد. در این مدت ممکن است کاربر تصور کند برنامه هنگ کرده و آن را ببندد. برای جلوگیری از این تصور، می‌خواهیم اگر کار بیش از سه ثانیه طول کشید، پیامی نمایش دهیم و از کاربر بخواهیم کمی بیشتر صبر کند. برای این کار تابع check_thread_status را نوشته‌ایم که یک ترد می‌گیرد و اگر بعد از سه ثانیه هنوز زنده بود، پیام می‌دهد.

import time
from threading import Thread

def task(name):
    print(f'task {name} started')
    time.sleep(1)
    print(f'task {name} finished')

def check_thread_status(thread):
    time.sleep(3)
    if thread.is_alive():
        print(f"{thread.name} is still running! please wait ...")

t1 = Thread(target=task, args=(1, ))
t2 = Thread(target=check_thread_status, args=(t1, ))

t1.start()
t2.start()

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

  • تابع task یک کار یک‌ثانیه‌ای را شبیه‌سازی می‌کند.
  • تابع check_thread_status یک ترد می‌گیرد و پس از سه ثانیه، اگر ترد هنوز زنده باشد، پیامی نمایش می‌دهد.
  • ترد t1 تابع task و ترد t2 تابع check_thread_status را اجرا می‌کند.

مشکل این کد این است که تسک اصلی در یک ثانیه تمام می‌شود، اما چون check_thread_status سه ثانیه sleep می‌کند و یک تردِ غیرdaemon است، برنامه منتظر آن می‌ماند و کل اجرا سه ثانیه طول می‌کشد؛ در حالی که کار مفید خیلی زودتر تمام شده بود.

حالا با یک تغییر کوچک، check_thread_status را به یک ترد daemon تبدیل می‌کنیم تا در پس‌زمینه اجرا شود و مانع پایان برنامه نگردد.

import time
from threading import Thread

def task(name):
    print(f'task {name} started')
    time.sleep(1)
    print(f'task {name} finished')

def check_thread_status(thread):
    time.sleep(3)
    if thread.is_alive():
        print(f"{thread.name} is still running! please wait ...")

t1 = Thread(target=task, args=(1, ))
t2 = Thread(target=check_thread_status, args=(t1, ), daemon=True)

t1.start()
t2.start()

حالا وقتی task بعد از یک ثانیه تمام می‌شود، تنها تردِ غیرdaemon (ترد اصلی) هم به پایان می‌رسد و برنامه بلافاصله بسته می‌شود؛ ترد daemon نیمه‌کاره رها می‌شود و دیگر سه ثانیه معطل نمی‌مانیم. دقیقاً همان رفتاری که برای یک کار فرعی می‌خواهیم: اگر کار اصلی تمام شد، منتظر کار فرعی نمی‌مانیم.

روش‌های تعریف یک ترد daemon

دو روش برای daemon کردن یک ترد وجود دارد.

۱. هنگام تعریف ترد

t2 = Thread(target=check_thread_status, args=(t1, ), daemon=True)

۲. پس از تعریف ترد (پیش از start)

t2 = Thread(target=check_thread_status, args=(t1, ))
t2.daemon = True

خواندن و تنظیم daemon

در نسخه‌های قدیمی پایتون دو متد برای این کار وجود داشت: isDaemon() که مقدار ویژگی daemon را برمی‌گرداند، و setDaemon() که یک مقدار boolean می‌گیرد و آن را تنظیم می‌کند. اما این متدها منسوخ (deprecated) شده‌اند و توصیه می‌شود مستقیماً با ویژگی daemon کار کنید (خواندن با t.daemon و تنظیم با t.daemon = True).

import time
from threading import Thread

def say_hi(name):
    time.sleep(1)
    print('hi', name)

t1 = Thread(target=say_hi, args=('Ali', ))
t2 = Thread(target=say_hi, args=('Firooze', ))
t3 = Thread(target=say_hi, args=('Hamid', ), daemon=True)
t4 = Thread(target=say_hi, args=('Saeed', ))

t4.daemon = True     # preferred
t4.setDaemon(True)   # deprecated

t1.start()
t2.start()
t3.start()
t4.start()

print('t1', t1.daemon)       # preferred
print('t1', t1.isDaemon())   # deprecated
print('t2', t2.daemon)       # preferred
print('t2', t2.isDaemon())   # deprecated
print('t3', t3.daemon)       # preferred
print('t3', t3.isDaemon())   # deprecated
print('t4', t4.daemon)       # preferred
print('t4', t4.isDaemon())   # deprecated

نکته‌ی مهم: زمان تنظیم daemon

ویژگی daemon را باید پیش از فراخوانی start تنظیم کنید. اگر بعد از استارت‌شدن ترد بخواهید آن را تغییر دهید، با خطای RuntimeError روبه‌رو می‌شوید. کد زیر خطا می‌دهد:

import time
from threading import Thread

def say_hi(name):
    time.sleep(1)
    print('hi', name)

t1 = Thread(target=say_hi, args=('mmreza', ))
t1.start()
t1.daemon = True   # RuntimeError: cannot set daemon status of active thread

نکته‌ی تکمیلی: daemon و join

یک نکته‌ی کاربردی که خوب است بدانید: چون تردهای daemon ممکن است هر لحظه و به‌صورت ناگهانی (هنگام پایان برنامه) بسته شوند، برای کارهایی که حتماً باید تا انتها اجرا شوند — مثل نوشتن کامل یک فایل یا بستن درست یک اتصال — از daemon استفاده نکنید، چون ممکن است وسط کار قطع شوند. daemon فقط برای کارهای فرعیِ قابل‌صرف‌نظر مناسب است. اگر لازم است منتظر پایان یک ترد بمانید، از join() استفاده کنید (که برای تردهای daemon هم کار می‌کند و تا پایان آن ترد صبر می‌کند).

خلاصه

ویژگیdaemon = Falsedaemon = True
حالت پیش‌فرضبلهخیر
اجرا در پس‌زمینهخیربله
پایان زندگیبرنامه‌ی اصلی تا پایان اجرای آن صبر می‌کند.اگر همه‌ی تردهای غیرdaemon تمام شوند، به‌صورت خودکار بسته می‌شود.
  • ترد daemon برای کارهای فرعیِ پس‌زمینه است که پایان‌نیافتنشان مشکلی ایجاد نمی‌کند.
  • daemon را با daemon=True هنگام ساخت یا با t.daemon = True پیش از start تنظیم کنید.
  • متدهای isDaemon() و setDaemon() منسوخ‌اند؛ مستقیماً با ویژگی daemon کار کنید.
  • تنظیم daemon پس از start خطای RuntimeError می‌دهد.
Please login to bookmark Close
پیشرفت شما در «دوره آموزش کانکارنسی در پایتون» (12%)
نظرات

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

12%
پیشرفت

سرفصل دوره

فهرست مطالب

سرفصل دوره

تمرین

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

پاسخ تمرین ها

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

اشتراک گذاری

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

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

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

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

تنظیمات

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