python从写定时器学习Thread

python 如何写一个定时器,循环定时做某一操作呢?

Timer 对象

from threading import Timer
def hello():
print "hello, world" t = Timer(10.0, hello)
t.start()

10秒后输出:

hello, world

重点研究 t = Timer(10.0, hello) 这句代码,python 提供了一个Timer 对象,它会在指定的时间后执行某一操作;它的完整形式:

class threading.Timer(interval, function, args=[], kwargs={})

interval 是时间间隔,function 是可调用的对象,argskwargs 会作为 function 的参数。

注意:这里只会执行一次 function,而不会一直定时执行,且 Timer 在执行操作的时候会创建一个新的线程。

Timer 在 python2 和 python3 有点区别:

# python2.7
def Timer(*args, **kwargs):
return _Timer(*args, **kwargs)
# python3.7
class Timer(Thread):
pass

在 python3,TimerThread 的子类;在 python2,_TimerThread 的子类,而 Timer 只是 _Timer 类的工厂方法。

上面的代码只会打印一次 hello, world 后退出,那么如何循环间隔打印呢?

粗陋的循环定时器

一种方法是在 function 里继续注册一个 Timer,这样就可以在下一个 interval 继续执行 function

from threading import Timer
def hello():
print "hello, world"
Timer(10.0, hello) .start() t = Timer(10.0, hello)
t.start()

每隔 10 秒输出一个 hello, world

达到效果了,但是这里面好像有点问题。回到 Timer 本身,它是一个 thread,每次循环间隔操作,系统都要创建一个线程,然后再回收,这对系统来说开销很大。如果时间间隔 interval 很短,系统会一下子创建很多线程,这些线程很难快速回收,导致系统内存和cpu资源被消耗掉。

所以不提倡在 function 里继续注册一个 Timer。

更 pythonic 循环定时器

这里有更 pythonic 的方法:

from threading import _Timer
def hello():
print "hello, world"
class RepeatingTimer(_Timer):
def run(self):
while not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.wait(self.interval)
t = RepeatingTimer(10.0, hello)
t.start()

重点研究 RepeatingTimer 类,它继承了 threading._Timer,但是重写了父类的 run 方法。这是 Python2 的写法,python3 中 RepeatingTimer 应该继承 threading.Timer

为什么要重写 Threadrun 方法?

_Timer 是一个 Thread 子类,我们先看看 Thread 类的 run 用法。

from threading import Thread
def hello():
print "hello, world"
# 继承 Thread
class MyThread(Thread):
# 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
def run(self):
hello()
t = MyThread()
t.start()

Thread 对象的完整定义:

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})

其中 run 方法代码:

class Thread(_Verbose):
def run(self):
try:
if self.__target:
self.__target(*self.__args, **self.__kwargs)
finally:
# Avoid a refcycle if the thread is running a function with
# an argument that has a member that points to the thread.
del self.__target, self.__args, self.__kwargs

标准的 run 方法用于执行用户传入构造函数的 target 方法。 子类可以重写 run 方法,把要执行的代码写到 run 里面,线程在创建后,用户调用 start() 方法会运行 run() 方法。

所以 RepeatingTimer 重写 _Timer 的 run() 方法,可以改变线程的执行体,当我们调用 RepeatingTimer 的 start() 方法时会执行我们重写的 run() 方法。

再看看 RepeatingTimer 类中的 while not self.finished.is_set() 语句,self.finished.is_set() 直到 True 才会退出循环,定时器才结束。finishedthreading.Event 对象。一个 Event 对象管理着一个 flag 标志,它能被 set() 方法设置为 True,也能被 clear() 方法设置为 False,调用 wait([timeout]) 线程会一直 sleep 到 flag 为 True 或超时时间到达。

我们知道定时器有一个 cancel() 方法可以提前取消操作。它其实是调用 Event.clear() 方法提前让 wait 方法结束等待,并且判断在 flag 为 true 的情况下不执行定时器操作。具体的代码:

class _Timer(Thread):
"""Call a function after a specified number of seconds:
t = Timer(30.0, f, args=[], kwargs={})
t.start()
t.cancel() # stop the timer's action if it's still waiting
""" def __init__(self, interval, function, args=[], kwargs={}):
Thread.__init__(self)
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.finished = Event() def cancel(self):
"""Stop the timer if it hasn't finished yet"""
self.finished.set() def run(self):
self.finished.wait(self.interval)
if not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.set()

所以 RepeatingTimer 的 run 方法会一直执行 while 循环体,在循环体了会执行用户传入的 function 对象,并等待指定的时间。当用户想退出定时器时,只需要调用 cancel 方法,将 flag 置为 True 便不会继续执行循环体了。这样便完成了一个还不错的循环定时器。

FAQ

  1. 准确定时问题

A:

self.function(*self.args, **self.kwargs)
self.finished.wait(self.interval)

有一个疑问,如果function方法是一个很耗时的过程,那么其实并没有实现准确的定时。比如function完成需要2s,interval设置10s,实际的定时间隔是2+10=12s。

Q:

分场景看吧,我们先把定时执行的过程简称为周期性事件。

  1. 如果周期事件没有执行完,就不执行下一个周期事件,那么上面的方法是推荐的;耗时如果比较长,比如 redis 的 serverCron 函数,就把持久化操作放在子进程去完成;
  2. 如果需要准确的定时,可以退化为 timer 里定义 timer 的方法,用多个 Thread 去执行;定时比较短的情况,此方法不推荐,会产生很多等待的 Thread,创建和回收都很耗资源,此时可以维护一个线程池避免这个问题

python从写定时器学习Thread的更多相关文章

  1. 学习Python到写poc其实没那么难

    现在,开始! 0x00 前言 今天刚刚把http://drops.wooyun.org/tips/12751放到收藏夹准备看的,然后又看到题主的这个问题.顺便观摩了1楼大神的博客,我这种炒鸡新手表示很 ...

  2. 开始写下自己的python的cocos2d, pyglet学习

    开始写下自己的python的cocos2d, pyglet学习 2014年01月18日 13:52:36 我要做程序达人 阅读数 9051更多 分类专栏: python的cocos2d和pyglet ...

  3. Python第十课学习

    Python第十课学习 www.cnblogs.com/yuanchenqi/articles/5828233.html 函数: 1 减少代码的重复 2 更易扩展,弹性更强:便于日后文件功能的修改 3 ...

  4. 第四百一十五节,python常用排序算法学习

    第四百一十五节,python常用排序算法学习 常用排序 名称 复杂度 说明 备注 冒泡排序Bubble Sort O(N*N) 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 ...

  5. Python入门到精通学习书籍推荐!

    1.Python基础教程(第2版 修订版)<Python基础教程(第2版修订版)>包括Python程序设计的方方面面,内容涉及的范围较广,既能为初学者夯实基础,又能帮助程序员提升技能,适合 ...

  6. python中confIgparser模块学习

    python中configparser模块学习 ConfigParser模块在python中用来读取配置文件,配置文件的格式跟windows下的ini配置文件相似,可以包含一个或多个节(section ...

  7. (转)Python新手写出漂亮的爬虫代码1——从html获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78156605 Python新手写出漂亮的爬虫代码1初到大数据学习圈子的同学可能对爬虫都有 ...

  8. selenium + python自动化测试unittest框架学习(二)

    1.unittest单元测试框架文件结构 unittest是python单元测试框架之一,unittest测试框架的主要文件结构: File >report >all_case.py &g ...

  9. python手写bp神经网络实现人脸性别识别1.0

    写在前面:本实验用到的图片均来自google图片,侵删! 实验介绍 用python手写一个简单bp神经网络,实现人脸的性别识别.由于本人的机器配置比较差,所以无法使用网上很红的人脸大数据数据集(如lf ...

随机推荐

  1. Spring @Scheduled定时任务的fixedRate,fixedDelay,cron的作用和不同

    一.   三种定时类型. 1.cron  --@Scheduled(cron="0/5 * * * *?") 当时间达到设置的时间会触发事件.上面那个例子会每5秒执行一次. 201 ...

  2. 最长公共上升子序列 O(n^2)

    #include <bits/stdc++.h> using namespace std; const int MAXN = 5005; int A[MAXN], B[MAXN], N, ...

  3. ent 基本使用十八 查询谓词

    ent 生成的代码包含了比较完整的查询谓词 字段谓词 Bool: =, != Numeric: =, !=, >, <, >=, <=, IN, NOT IN Time: =, ...

  4. 14-网页,网站,微信公众号基础入门(网页版MQTT,小试牛刀)

    https://www.cnblogs.com/yangfengwu/p/11192639.html 抱歉哈...最近由于做板子,,教程的进度落下了... 这些天总共做了还几块板子 首先对当前这个教程 ...

  5. shell 给文件每一行都添加指定字符串

    [admin@localhost file]$ cat file hello hello hello hello hello [admin@localhost file]$ cat test.sh # ...

  6. 云服务器搭建JDK+Tomcat+MySQL环境

    一.首先租赁一台云服务器(阿里云服务器或者腾讯云服务器) 其实可以在windows电脑上使用VMware workstation来安装虚拟机进行操作,毕竟云服务器低配也是很贵的.不过可以使用学生价去租 ...

  7. 第10组 Alpha冲刺(3/4)

    队名:凹凸曼 组长博客 作业博客 组员实践情况 童景霖 过去两天完成了哪些任务 文字/口头描述 继续学习Android studio和Java 制作登录注册界面前端 展示GitHub当日代码/文档签入 ...

  8. 11月1号开学! 《jmeter性能测试实战》崭新亮相!

    课程介绍 第10期<jmeter性能测试实战>课程,11月2号开学!全新改版,和之前的课程框架完全不同 主讲老师:飞天小子 上课方式:每周六周日晚8点到10点,QQ群视频在线直播教学 本期 ...

  9. SpringBoot之文件上传体积过大问题(解决方案)

    错误信息如下(关键): org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the re ...

  10. return、reutrn false、e.preventDefault、e.stopPropagation、e.stopImmediatePropagation的区别

    return var i = function(){ return } console.log(i())//undefined return的主要作用是阻止函数继续执行,直接返回undefined r ...