python schedule多线程_【Python】基于schedule模块实现在同一时刻启动多个定时任务...

最近需要做定时任务,同事推荐了Python的一个库叫做schedule,用它来做定时还是比较简单的。

比如,像这样傻瓜式的:

schedule.every().day.at("10:36").do(daily_job_A)

简单运用在我的代码,但是前两天运行中发现一个现象:如果定义了两个启动时间相差1min的task且前1个task不能在1min内执行完毕,那么后一个将一直被阻塞直到前一个执行完,也就是说定时到了也不能启动。

由于我的task是爬虫程序会运行很久,这样一个接一个当然不行。

不过为了验证,先写个demo复现一下上述现象是否确确实实存在:

import schedule

import time

def daily_job_A():

print("A启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(300)

print("A完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def weekly_job_A():

print("A启动2" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(300)

print("A2完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def daily_job_B():

print("B启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(300)

print("B完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def weekly_job_B():

print("B启动2" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(300)

print("B2完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

if __name__ == "__main__":

schedule.every().day.at("10:36").do(daily_job_A)

schedule.every().sunday.at("10:37").do(weekly_job_A)

schedule.every().day.at("11:38").do(daily_job_B)

schedule.every().sunday.at("11:39").do(weekly_job_B)

while True:

schedule.run_pending()

time.sleep(1)运行效果……A2本来该在10:37启动,但实际上被阻塞到A完毕

好了,现象确实存在。

那怎么解决呢?

一开始是准备从schedule的层面解决,因为按照这样的调用方式schedule显然没有对每个任务去开新的进程,所以这些任务们只好排队,等着这一个进程有空;但转念一想,开新进程这事也可以在自己的应用层面解决——把定时任务的任务编程新开子进程。

(一般定时任务是在操作系统层面做到的吧,这里用schecule其实就是假设操作系统层面做不到的话,在python层面去做。也是借鉴这个思路,schedule的API这个层面解决不了,再往上层去做;TCP协议有一些问题,于是在应用层做一些事情……)

试一试,修改如下:

(本来准备用subprocess.call结果发现是命令行形式的启动,于是用了multiprocessing.Process)

import schedule

import time

from multiprocessing import Process

def daily_job_A():

print("A启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

Process = time.sleep(120)

print("A完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def weekly_job_A():

print("A2启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(120)

print("A2完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def daily_job_B():

print("B启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(120)

print("B完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def weekly_job_B():

print("B2启动" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

time.sleep(120)

print("B2完毕" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

def daily_job():

p1 = Process(target=daily_job_A)

p2 = Process(target=daily_job_B)

p1.start()

print("p1.start")

p2.start()

print("p2.start")

def weekly_job():

p3 = Process(target=weekly_job_A)

p4 = Process(target=weekly_job_B)

p3.start()

print("p3.start")

p4.start()

print("p4.start")

if __name__ == "__main__":

schedule.every().day.at("11:9").do(daily_job)

schedule.every().sunday.at("11:10").do(weekly_job)

while True:

schedule.run_pending()

time.sleep(1)

这下成功啦,效果如下:A和B是同时启动,A2和B2也是同时启动,而且在A和B没有结束的时候A2和B2也启动了

【实现要点】

schedule对各个定时任务是按阻塞式执行,我没有改变这一点,但是改变了定时任务,让定时任务从需要执行很久变成很快执行完毕——具体来说,这里的A和B是我们想要定时的任务,但是任务执行会花去2min,如果直接把A和B当做定时任务第二个就会被阻塞到2min以后才开始,所以类似于偷换了一个概念——把“启动A”和“启动B”做成了定时任务。

当定时任务的内容只是p1.start(),也就是【启动】一个子进程,这是瞬间可以做完的事情。至于子进程p1自己要执行多久都没有关系,因为这里没有p1.join(),主进程就不会等子进程结束,自己继续往下执行,去启动p2, 然后返回,因为对主进程来说它的所有步骤在这个时候都执行完毕了。

【另外注意到 主进程结束 并没有影响子进程】

这很好。因为以前写例子都会在p.start()之后跟上p.join(),意思是等子进程结束以后主进程再往下走。而这里希望主进程不要等待,所以没有设p.join(),开始会有担心子进程会随父进程而消失而消失,毕竟是派生出来的,结果证明不会。

应用到自己的代码,改进!!!

刚才是windows环境,保险起见,还是去ubuntu上试一下,以防有坑:

没问题的(*^▽^*)

最后附一个可以打印进程PID的例子备查。虽然和要解决的问题没有太大关系:

from multiprocessing import Process

import os

def info(title):

print(title)

print('module name:', __name__)

if hasattr(os, 'getppid'): # only available on Unix

print('parent process:', os.getppid())

print('process id:', os.getpid())

def f(name):

info('function f')

print('hello', name)

if __name__ == '__main__':

info('main line')

p = Process(target=f, args=('bob',))

p.start()

p.join()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值