FA-TOOLS — Header Component

زمان‌بندی و اجرای خودکار کد پایتون با schedule

خلاصه کاربردی مقاله:

  • کتابخانه schedule ساده‌ترین ابزار پایتون برای خودکارسازی کارهای دوره‌ای بدون درگیر شدن با تنظیمات پیچیده سیستم‌عامل است.
  • این کتابخانه به‌صورت پیش‌فرض تک‌نخی (Single-thread) است؛ برای کارهای سنگین باید از Threading استفاده کنید تا اجرای یک وظیفه، بقیه را متوقف نکند.
  • با پیاده‌سازی یک دکوراتور خطاگیر (Error Handler)، می‌توانید از کرش کردن کل پروسه در صورت رخ دادن باگ در توابع جلوگیری کنید.

آیا می‌خواهید اسکریپت پایتون شما هر روز راس ساعت مشخصی اجرا شود، یا نیاز دارید یک کار تکراری مثل پشتیبان‌گیری از دیتابیس را هر ۱۰ دقیقه یک‌بار به طور خودکار انجام دهید؟ کتابخانه schedule در پایتون یک ابزار فوق‌العاده سبک، سریع و با فهم آسان است که به شما اجازه می‌دهد زمان‌بندی فرآیندها را مستقیماً داخل کدهای خود مدیریت کنید. در این مقاله به صورت گام‌به‌گام و با سناریوهای واقعی، یاد می‌گیرید که چگونه کدهای خود را به ماشین‌های خودکار و بدون نیاز به نظارت تبدیل کنید.

۱. کتابخانه schedule در پایتون چیست؟

زمان‌بندی و اجرای خودکار کد پایتون با schedule — تصویر 1

کتابخانه schedule یک ابزار سبک و فوق‌العاده کاربرپسند برای مدیریت و اجرای زمان‌بندی‌شده وظایف (Jobs) در پایتون است. این کتابخانه با بهره‌گیری از سینتکس شبیه به زبان طبیعی انسان، به توسعه‌دهنده اجازه می‌دهد بدون نیاز به یادگیری فرمت‌های پیچیده‌ای مثل Cron-jobs در لینوکس، کارهای خود را زمان‌بندی کند.

مهم‌ترین ویژگی این کتابخانه، عدم وابستگی آن به سیستم‌عامل است؛ یعنی کدی که با schedule می‌نویسید، روی ویندوز، لینوکس و مک به یک شکل و بدون تغییر کار خواهد کرد. برای توسعه سریع پروژه‌ها، استفاده از آرشیو تکه‌کدهای پایتون می‌تواند به شما در پیاده‌سازی سریع‌تر این سناریوها کمک کند.

۲. نصب و راه‌اندازی اولیه

زمان‌بندی و اجرای خودکار کد پایتون با schedule — تصویر 2

کتابخانه schedule جزو پکیج‌های پیش‌فرض پایتون نیست و باید آن را نصب کنید. نصب این پکیج بسیار ساده است و تنها با یک دستور در ترمینال یا خط فرمان انجام می‌شود:

pip install schedule

پس از اتمام نصب، برای اطمینان از صحت نصب، یک فایل پایتون جدید بسازید و دستور import schedule را در آن بنویسید. اگر برنامه‌ بدون خطا اجرا شد، یعنی همه‌چیز آماده است.

۳. ساختار دستورات و سینتکس پایه

زمان‌بندی و اجرای خودکار کد پایتون با schedule — تصویر 3

زیبایی کتابخانه schedule در سینتکس خوانا و زنجیره‌ای (Method Chaining) آن است. به کدهای زیر دقت کنید؛ تقریباً بدون نیاز به دانش برنامه‌نویسی هم می‌توان کارکرد هر خط را متوجه شد:

import schedule
import time

def send_report():
    print(“گزارش روزانه ارسال شد.”)

def backup_db():
    print(“پشتیبان‌گیری از دیتابیس انجام شد.”)

# زمان‌بندی وظایف
schedule.every(10).seconds.do(send_report) # هر ۱۰ ثانیه
schedule.every().hour.do(backup_db) # هر ساعت یک‌بار
schedule.every().day.at(“14:30”).do(send_report) # هر روز ساعت ۱۴:۳۰
schedule.every().monday.do(backup_db) # دوشنبه‌ها هر هفته

# حلقه بی‌نهایت برای زنده نگه داشتن برنامه و بررسی وظایف معوقه
while True:
    schedule.run_pending()
    time.sleep(1)

نکته حیاتی: متد schedule.run_pending() بررسی می‌کند که آیا زمان اجرای کاری فرارسیده است یا خیر. اگر حلقه while True را انتهای برنامه قرار ندهید، برنامه بلافاصله پس از تعریف کردن زمان‌بندی‌ها بسته می‌شود و هیچ کاری عملاً اجرا نخواهد شد.

۴. ارسال آرگومان به توابع زمان‌بندی‌شده

زمان‌بندی و اجرای خودکار کد پایتون با schedule — تصویر 4

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

روش درست این است که آرگومان‌ها را به عنوان پارامترهای بعدی متد do وارد کنید:

import schedule
import time

def greet(name, role):
    print(f“سلام {name} ({role})، خوش آمدید!”)

# ارسال آرگومان‌ها به ترتیب تعریف در تابع
schedule.every(5).minutes.do(greet, name=“علی”, role=“مدیر سیستم”)

while True:
    schedule.run_pending()
    time.sleep(1)

۵. اجرای همزمان (Parallel) وظایف با Threading

زمان‌بندی و اجرای خودکار کد پایتون با schedule — تصویر 5

یکی از بزرگ‌ترین چالش‌ها و محدودیت‌های پنهان کتابخانه schedule این است که به صورت تک‌نخی کار می‌کند. یعنی اگر وظیفه اول شما نیاز به ۳۰ ثانیه پردازش داشته باشد، اجرای وظیفه دومی که همزمان با آن برنامه‌ریزی شده، تا پایان کار اول به تعویق می‌افتد.

برای حل این مشکل، بهترین راهکار استفاده از ماژول استاندارد threading پایتون برای اجرای موازی کارها است. ساختار زیر تضمین می‌کند هر وظیفه در یک ترد مجزا باز شده و هیچ تاخیری روی بقیه کارها ایجاد نمی‌شود:

import schedule
import time
import threading

def run_threaded(job_func):
    job_thread = threading.Thread(target=job_func)
    job_thread.start()

def heavy_task():
    print(“شروع یک کار سنگین محاسباتی…”)
    time.sleep(10) # شبیه‌سازی تاخیر ده ثانیه‌ای
    print(“کار سنگین تمام شد!”)

def quick_task():
    print(“اجرای یک کار سریع کمتر از ثانیه.”)

# به جای اجرای مستقیم، تابع را داخل ترد اجرا می‌کنیم
schedule.every(5).seconds.do(run_threaded, heavy_task)
schedule.every(2).seconds.do(run_threaded, quick_task)

while True:
    schedule.run_pending()
    time.sleep(1)

۶. مقایسه schedule با سایر روش‌های زمان‌بندی

برای انتخاب بهترین ابزار، باید تفاوت‌های پکیج schedule را با ابزارهای قدرتمندتری مثل APScheduler و سیستم‌عاملی مثل Cron بدانید. جدول زیر یک مقایسه اجمالی و واقع‌بینانه ارائه می‌دهد:

معیار ارزیابی ابزار schedule (پایتون) سیستم Cron (لینوکس) کتابخانه APScheduler
سهولت در یادگیری بسیار عالی (مثل زبان انسان) متوسط (فرمت عددی ستاره‌دار) سخت (مفاهیم پیچیده شی‌گرا)
مستقل بودن از سیستم‌عامل بله (روی هر سیستم‌عاملی کار می‌کند) خیر (مخصوص یونیکس/لینوکس) بله
نیاز به دیتابیس (Persistence) پشتیبانی نمی‌کند (فقط داخل حافظه موقت) مستقل از رم پشتیبانی کامل (SQLAlchemy, MongoDB)
مناسب برای پروژه با ابعاد کوچک تا متوسط (میکروسرویس‌ها) تسک‌های کل سیستم‌عامل سازمانی و توزیع‌شده بزرگ

۷. مدیریت خطا و جلوگیری از توقف برنامه

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

برای جلوگیری از این فاجعه، بهتر است کلاس پیش‌فرض یا متدی سفارشی تعریف کنید تا هرگونه خطا را مهار کرده و زمان‌بندی را زنده نگه دارد. در ادامه نحوه ساخت یک دکوراتور خطاگیر امن را مشاهده می‌کنید:

import schedule
import time
import functools

def catch_exceptions(cancel_on_failure=False):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                print(f“[خطا] مشکلی رخ داد: {e}”)
                if cancel_on_failure:
                    return schedule.CancelJob
        return wrapper
    return decorator

@catch_exceptions(cancel_on_failure=False)
def buggy_job():
    # تقسیم بر صفر برای شبیه‌سازی رخ دادن خطا
    result = 1 / 0

schedule.every(3).seconds.do(buggy_job)

while True:
    schedule.run_pending()
    time.sleep(1)

با استفاده از دکوراتور فوق، حتی اگر خطا در کد رخ دهد، در ترمینال چاپ می‌شود اما لوپِ اصلی برنامه متوقف نخواهد شد و سایر تسک‌ها به حیات خود ادامه می‌دهند.

۸. اجرای دائمی برنامه در پس‌زمینه سرور

وقتی کد پایتون خود را در سیستم‌عامل‌های سروری لینوکس (مانند اوبونتو) قرار می‌دهید، نمی‌توانید ترمینال را همیشه باز نگه دارید. شما باید اسکریپت را به صورت پس‌زمینه (Daemon Process) راه‌اندازی کنید.

استفاده از ابزار Systemd لینوکس

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

  1. یک فایل جدید با پسوند .service ایجاد کنید:
    sudo nano /etc/systemd/system/py-scheduler.service
  2. پیکربندی زیر را داخل آن قرار داده و ذخیره کنید:
    [Unit]
    Description=My Python Scheduler Daemon
    After=network.target

    [Service]
    Type=simple
    User=root
    WorkingDirectory=/home/user/project
    ExecStart=/usr/bin/python3 /home/user/project/main.py
    Restart=always

    [Install]
    WantedBy=multi-user.target

  3. دستورات زیر را برای فعال‌سازی و شروع به کار سرویس وارد کنید:
    sudo systemctl daemon-reload
    sudo systemctl enable py-scheduler.service
    sudo systemctl start py-scheduler.service

۹. عیب‌یابی سریع مشکلات رایج

در این بخش به بررسی رایج‌ترین مشکلاتی که توسعه‌دهندگان هنگام کار با پکیج schedule تجربه می‌کنند، به همراه راه‌حل قطعی آن‌ها می‌پردازیم:

مشکل اول: چرا برنامه کارهای من را انجام نمی‌دهد و بلافاصله بسته می‌شود؟
علت: فراموش کرده‌اید که حلقه بی‌نهایت while True را با دستور run_pending() انتهای اسکریپت اضافه کنید.
راه‌حل: اطمینان حاصل کنید که این سه خط همواره در بخش نهایی فایل اجرایی شما حضور دارند.
مشکل دوم: تاخیر در اجرای کارهای بعدی بعد از سنگین شدن یک وظیفه
علت: ماهیت تک‌رشته‌ای کتابخانه به تسک‌های طولانی اجازه تداخل با سایر زمان‌بندی‌ها را می‌دهد.
راه‌حل: همان‌طور که در بخش پنجم آموزش داده شد، از ماژول threading پایتون جهت موازی‌سازی اجرای تسک‌ها استفاده نمایید.
مشکل سوم: وظایف پس از ری‌استارت شدن سرور کاملاً پاک می‌شوند
علت: این ماژول تمام زمان‌بندی‌ها را در RAM ذخیره می‌کند؛ هیچ دیتابیسی برای بازیابی تسک‌ها وجود ندارد.
راه‌حل: در صورتی که ذخیره‌سازی وضعیت وظایف (Persistence) برای شما واجب است، از فریم‌ورک‌های قدرتمندتری مثل APScheduler استفاده کنید.

۱۰. پرسش‌های متداول

آیا می‌توانم از schedule در وب‌سایت‌های جنگو (Django) یا فلسک (Flask) استفاده کنم؟
بله، هر چند استفاده از آن در پروژه‌های وب سبک پاسخگو است، اما برای پروژه‌های بزرگ صنعتی و جاب‌های حساس در فریم‌ورک‌های وب مثل جنگو، به شدت توصیه می‌شود از Celery به همراه Redis یا RabbitMQ استفاده کنید.

آیا کتابخانه schedule از منطقه‌های زمانی (Timezone) پشتیبانی می‌کند؟
خیر، این کتابخانه به طور پیش‌فرض زمان لوکال سروری که روی آن اجرا می‌شود را ملاک قرار می‌دهد. در صورت تغییر ساعت سرور یا ساعت تابستانه (DST)، باید متناسب با ساعت جدید مجدداً اسکریپت را مدیریت یا همگام کنید.

چگونه یک وظیفه زمان‌بندی شده را پس از یک‌بار اجرا متوقف یا لغو کنیم؟
شما می‌توانید داخل بدنه تابعی که فراخوانی می‌شود، در صورتی که شرط مشخصی برقرار شد، عبارت return schedule.CancelJob را بازگردانید. پایتون با دیدن این مقدار بازگشتی، آن زمان‌بندی را برای همیشه غیرفعال می‌کند.

آیا مصرف حافظه و CPU این کتابخانه بالا است؟
خیر، کتابخانه schedule بسیار سبک است. با این حال استفاده از متد time.sleep(1) در بدنه لوپ بی‌نهایت ضروری است؛ چرا که عدم استفاده از آن باعث درگیر شدن مداوم پردازنده با نرخ ۱۰۰ درصد خواهد شد.

Table of Contents

آخرین نوشته‌ها

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *