言
APScheduler是Python中知名的定时任务框架,可以很方面的满足定时执行或周期性执行程序任务等需求,类似于Linux上的crontab,但比crontab要更加强大,该框架不仅可以添加、删除定时任务,还提供多种持久化任务的功能。
APScheduler弱分布式的框架,因为每个任务对象都存储在当前节点中,只能通过人肉的形式实现分布式,如利用Redis来做。
剖析BackgroundScheduler
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
from datetime import datetime
import time
import os
from apscheduler.schedulers.background import BackgroundScheduler
def tick():
print ( 'Tick! The time is: %s' % datetime.now())
if __name__ = = '__main__' :
scheduler = BackgroundScheduler()
scheduler.add_job(tick, 'interval' , seconds = 3 ) # 添加一个任务,3秒后运行
scheduler.start()
print ( 'Press Ctrl+{0} to exit' . format ( 'Break' if os.name = = 'nt' else 'C' ))
try :
# 这是在这里模拟应用程序活动(使主线程保持活动状态)。
while True :
time.sleep( 2 )
except (KeyboardInterrupt, SystemExit):
# 关闭调度器
scheduler.shutdown()
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# apscheduler/schedulers/background.py
class BackgroundScheduler(BlockingScheduler):
_thread = None
def _configure( self , config):
self ._daemon = asbool(config.pop( 'daemon' , True ))
super ()._configure(config)
def start( self , * args, * * kwargs):
# 创建事件通知
# 多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。
self ._event = Event()
BaseScheduler.start( self , * args, * * kwargs)
self ._thread = Thread(target = self ._main_loop, name = 'APScheduler' )
# 设置为守护线程,Python主线程运行完后,直接结束不会理会守护线程的情况,
# 如果是非守护线程,Python主线程会在运行完后,等待其他非守护线程运行完后,再结束
self ._thread.daemon = self ._daemon # daemon 是否为守护线程
self ._thread.start() # 启动线程
def shutdown( self , * args, * * kwargs):
super ().shutdown( * args, * * kwargs)
self ._thread.join()
del self ._thread
|
剖析add_job方法
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# apscheduler/schedulers/base.py/BaseScheduler
def add_job( self , func, trigger = None , args = None , kwargs = None , id = None , name = None ,
misfire_grace_time = undefined, coalesce = undefined, max_instances = undefined,
next_run_time = undefined, jobstore = 'default' , executor = 'default' ,
replace_existing = False , * * trigger_args):
job_kwargs = {
'trigger' : self ._create_trigger(trigger, trigger_args),
'executor' : executor,
'func' : func,
'args' : tuple (args) if args is not None else (),
'kwargs' : dict (kwargs) if kwargs is not None else {},
'id' : id ,
'name' : name,
'misfire_grace_time' : misfire_grace_time,
'coalesce' : coalesce,
'max_instances' : max_instances,
'next_run_time' : next_run_time
}
# 过滤
job_kwargs = dict ((key, value) for key, value in six.iteritems(job_kwargs) if
value is not undefined)
# 实例化具体的任务对象
job = Job( self , * * job_kwargs)
# Don't really add jobs to job stores before the scheduler is up and running
with self ._jobstores_lock:
if self .state = = STATE_STOPPED:
self ._pending_jobs.append((job, jobstore, replace_existing))
self ._logger.info( 'Adding job tentatively -- it will be properly scheduled when '
'the scheduler starts' )
else :
self ._real_add_job(job, jobstore, replace_existing)
return job
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
from datetime import datetime
import time
import os
from apscheduler.schedulers.background import BackgroundScheduler
def tick():
print ( 'Tick! The time is: %s' % datetime.now())
if __name__ = = '__main__' :
scheduler = BackgroundScheduler()
scheduler.add_job(tick, 'interval' , seconds = 3 ) # 添加一个任务,3秒后运行
scheduler.start()
print ( 'Press Ctrl+{0} to exit' . format ( 'Break' if os.name = = 'nt' else 'C' ))
try :
# 这是在这里模拟应用程序活动(使主线程保持活动状态)。
while True :
time.sleep( 2 )
except (KeyboardInterrupt, SystemExit):
# 关闭调度器
scheduler.shutdown()
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# apscheduler/schedulers/background.py
class BackgroundScheduler(BlockingScheduler):
_thread = None
def _configure( self , config):
self ._daemon = asbool(config.pop( 'daemon' , True ))
super ()._configure(config)
def start( self , * args, * * kwargs):
# 创建事件通知
# 多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。
self ._event = Event()
BaseScheduler.start( self , * args, * * kwargs)
self ._thread = Thread(target = self ._main_loop, name = 'APScheduler' )
# 设置为守护线程,Python主线程运行完后,直接结束不会理会守护线程的情况,
# 如果是非守护线程,Python主线程会在运行完后,等待其他非守护线程运行完后,再结束
self ._thread.daemon = self ._daemon # daemon 是否为守护线程
self ._thread.start() # 启动线程
def shutdown( self , * args, * * kwargs):
super ().shutdown( * args, * * kwargs)
self ._thread.join()
del self ._thread
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# apscheduler/schedulers/base.py/BaseScheduler
def add_job( self , func, trigger = None , args = None , kwargs = None , id = None , name = None ,
misfire_grace_time = undefined, coalesce = undefined, max_instances = undefined,
next_run_time = undefined, jobstore = 'default' , executor = 'default' ,
replace_existing = False , * * trigger_args):
job_kwargs = {
'trigger' : self ._create_trigger(trigger, trigger_args),
'executor' : executor,
'func' : func,
'args' : tuple (args) if args is not None else (),
'kwargs' : dict (kwargs) if kwargs is not None else {},
'id' : id ,
'name' : name,
'misfire_grace_time' : misfire_grace_time,
'coalesce' : coalesce,
'max_instances' : max_instances,
'next_run_time' : next_run_time
}
# 过滤
job_kwargs = dict ((key, value) for key, value in six.iteritems(job_kwargs) if
value is not undefined)
# 实例化具体的任务对象
job = Job( self , * * job_kwargs)
# Don't really add jobs to job stores before the scheduler is up and running
with self ._jobstores_lock:
if self .state = = STATE_STOPPED:
self ._pending_jobs.append((job, jobstore, replace_existing))
self ._logger.info( 'Adding job tentatively -- it will be properly scheduled when '
'the scheduler starts' )
else :
self ._real_add_job(job, jobstore, replace_existing)
return job
|
接着做了一个过滤,然后将参数传入Job类,完成任务对象的实例化。
随后的逻辑比较简单,先判断是否可以拿到self._jobstores_lock锁,它其实是一个可重入锁,Python中,可重入锁的实现基于普通互斥锁,只是多了一个变量用于计数,每加一次锁,该变量加一,每解一次锁该变量减一,只有在该变量为0时,才真正去释放互斥锁。
获取到锁后,先判断当前调度器的状态,如果是STATE_STOPPED(停止状态)则将任务添加到_pending_jobs待定列表中,如果不是停止状态,则调用_real_add_job方法,随后返回job对象。
其实_real_add_job方法才是真正的将任务对象job添加到指定存储后端的方法。
当任务对象添加到指定存储后端后(默认直接存到内存中),调度器就会去取来执行。
回到example代码中,执行完调度器的add_job方法后,紧接着便执行调度器的start方法
更多技术咨询可关注:itheimaGZ获得