单线程多定时任务

  前言:公司业务需求,实例当中大量需要启动定时器的操作;大家都知道python中的定时器用的是threading.Timer,每当启动一个定时器时,程序内部起了一个线程,定时器触发执行结束后,线程自动销毁;这里就涉及到一个问题,如果同时有大量启动定时器的需求时,内部线程过多,程序肯定就崩了,有没有启一个线程就能完成定时器的操作呢?网上查了一些资料,还没有看到能解决目前问题的现成代码,不如自己搞一个试试

1、初始版本:

思路:定时器,说白了就是延时执行指定的程序,目前自己重构python里面的定时器不太现实,能力达不到,所以延时操作时还得用到系统定时器,不过我们可以改一下规则;把所有要进行定时操作的程序添加到特定列表中,把列表中定时时间最短程序拿出来,进行threading.Timer(time,callback)绑定,等时间超时触发自定义的callback,执行刚刚列表取出的程序;然后把时间更新,再次把列表中时间最短的程序拿出了,继续threading.Timer绑定,不断的迭代循环;当有新的定时任务加入到列表时,把当前的threading.Timer绑定取消,更新列表中的时间,再次取出最短时间,进行threading.Timer绑定......

代码:

  1. import threading
  2. import time
  3.  
  4. class Timer():
  5. '''单线程下的定时器'''
  6.  
  7. def __init__(self):
  8. self.queues = []
  9. self.timer = None
  10. self.last_time = time.time()
  11.  
  12. def start(self):
  13. item = self.get()
  14. if item:
  15. self.timer = threading.Timer(item[0],self.execute)
  16. self.timer.start()
  17.  
  18. def add(self,item):
  19. print('add',item)
  20. self.flush_time()
  21. self.queues.append(item)
  22. self.queues.sort(key=lambda x:x[0])
  23.  
  24. if self.timer:
  25. self.timer.cancel()
  26. self.timer = None
  27. self.start()
  28.  
  29. def get(self):
  30. item = None
  31. if len(self.queues) > 0:
  32. item = self.queues[0]
  33. return item
  34.  
  35. def pop(self):
  36. item = None
  37. if len(self.queues) > 0:
  38. item = self.queues.pop(0)
  39. return item
  40.  
  41. def flush_time(self):
  42. curr_time = time.time()
  43. for i in self.queues:
  44. i[0] = i[0] - (curr_time - self.last_time)
  45. self.last_time = curr_time
  46.  
  47. def execute(self):
  48. # if self.timer:
  49. # self.timer.cancel()
  50. # self.timer = None
  51. item = self.pop()
  52. self.flush_time()
  53. if item:
  54. callback = item[1]
  55. args = item[0]
  56. callback(args)
  57. self.start()

执行及输出:

  1. if __name__ == '__main__':
  2. # 检测线程数
  3. def func():
  4. while True:
  5. print(threading.active_count())
  6. time.sleep(1)
  7.  
  8. f1 = threading.Thread(target=func)
  9. f1.start()
  10.  
  11. import logging
  12. logging.basicConfig(level=logging.INFO,format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %H:%M:%S [%A]")
  13. def func1(*args):
  14. logging.info('func1 %s'%args)
  15. # time.sleep(5)
  16.  
  17. def func2(*args):
  18. logging.info('func2 %s' % args)
  19. # time.sleep(5)
  20. def func3(*args):
  21. logging.info('func3 %s' % args)
  22. # time.sleep(5)
  23.  
  24. def func4(*args):
  25. logging.info('func4 %s' % args)
  26. # time.sleep(5)
  27.  
  28. def func5(*args):
  29. logging.info('func5 %s' % args)
  30. # time.sleep(5)
  31.  
  32. # 测试
  33. t1 = Timer()
  34. logging.info('start')
  35. t1.add([5,func1])
  36. time.sleep(0.5)
  37. t1.add([4,func2])
  38. time.sleep(0.5)
  39. t1.add([3,func3])
  40. time.sleep(0.5)
  41. t1.add([2,func4])
  42. time.sleep(0.5)
  43. t1.add([1,func5])
  44. time.sleep(5)
  45. t1.add([1,func1])
  46. t1.add([2,func2])
  47. t1.add([3,func3])
  48. t1.add([4,func4])
  49. t1.add([5,func5])
  50.  
  51. # 输出
  52. #
  53. # 07/27/2017 10:36:47 [Thursday] start
  54. # add [5, <function func1 at 0x000000D79FC77E18>]
  55. # add [4, <function func2 at 0x000000D79FCA8488>]
  56. #
  57. # add [3, <function func3 at 0x000000D79FCA8510>]
  58. # add [2, <function func4 at 0x000000D79FCA8598>]
  59. #
  60. # add [1, <function func5 at 0x000000D79FCA8620>]
  61. #
  62. # 07/27/2017 10:36:50 [Thursday] func5 1
  63. # 07/27/2017 10:36:51 [Thursday] func4 0.498349666595459
  64. #
  65. # 07/27/2017 10:36:51 [Thursday] func3 0.49782633781433105
  66. # 07/27/2017 10:36:52 [Thursday] func2 0.49848270416259766
  67. #
  68. # 07/27/2017 10:36:52 [Thursday] func1 0.48449039459228516
  69. #
  70. #
  71. # add [1, <function func1 at 0x000000D79FC77E18>]
  72. # add [2, <function func2 at 0x000000D79FCA8488>]
  73. # add [3, <function func3 at 0x000000D79FCA8510>]
  74. # add [4, <function func4 at 0x000000D79FCA8598>]
  75. # add [5, <function func5 at 0x000000D79FCA8620>]
  76. #
  77. # 07/27/2017 10:36:55 [Thursday] func1 0.9990766048431396
  78. #
  79. # 07/27/2017 10:36:56 [Thursday] func2 0.9988017082214355
  80. #
  81. # 07/27/2017 10:36:57 [Thursday] func3 0.99928879737854
  82. # 07/27/2017 10:36:58 [Thursday] func4 0.9991350173950195
  83. #
  84. #
  85. # 07/27/2017 10:36:59 [Thursday] func5 0.9988160133361816

执行代码

注:查看代码输出,所有的定时器都按照标定的时间依次执行,非常完美,一切看起来很美好,只是看起来,呵呵哒,当你把func里面的time.sleep(5)启用后,线程数蹭蹭的上来了;原因是上个定时器callback还是执行中,下个定时器已经启动了,这时就又新增了一个线程,哎,失败

2、修订版本

思路:利用生成者消费者模型,用到threading.Condition条件变量;强制永远启用的是一个Timer!

代码:

  1. import time
  2. import threading
  3. import logging
  4.  
  5. class NewTimer(threading.Thread):
  6. '''单线程下的定时器'''
  7. def __init__(self):
  8. super().__init__()
  9. self.queues = []
  10. self.timer = None
  11. self.cond = threading.Condition()
  12.  
  13. def run(self):
  14. while True:
  15. # print('NewTimer',self.queues)
  16. self.cond.acquire()
  17. item = self.get()
  18. callback = None
  19. if not item:
  20. logging.info('NewTimer wait')
  21. self.cond.wait()
  22. elif item[0] <= time.time():
  23. new_item = self.pop()
  24. callback = new_item[1]
  25. else:
  26. logging.info('NewTimer start sys timer and wait')
  27. self.timer = threading.Timer(item[0]-time.time(),self.execute)
  28. self.timer.start()
  29. self.cond.wait()
  30. self.cond.release()
  31.  
  32. if callback:
  33. callback(item[0])
  34.  
  35. def add(self, item):
  36. # print('add', item)
  37. self.cond.acquire()
  38. item[0] = item[0] + time.time()
  39. self.queues.append(item)
  40. self.queues.sort(key=lambda x: x[0])
  41. logging.info('NewTimer add notify')
  42. if self.timer:
  43. self.timer.cancel()
  44. self.timer = None
  45. self.cond.notify()
  46. self.cond.release()
  47.  
  48. def pop(self):
  49. item = None
  50. if len(self.queues) > 0:
  51. item = self.queues.pop(0)
  52. return item
  53.  
  54. def get(self):
  55. item = None
  56. if len(self.queues) > 0:
  57. item = self.queues[0]
  58. return item
  59.  
  60. def execute(self):
  61. logging.info('NewTimer execute notify')
  62. self.cond.acquire()
  63. self.cond.notify()
  64. self.cond.release()

执行及输出:

  1. if __name__ == '__main__':
  2. def func():
  3. while True:
  4. print(threading.active_count())
  5. time.sleep(1)
  6.  
  7. f1 = threading.Thread(target=func)
  8. f1.start()
  9. logging.basicConfig(level=logging.INFO,format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %H:%M:%S [%A]")
  10.  
  11. newtimer = NewTimer()
  12. newtimer.start()
  13.  
  14. def func1(*args):
  15. logging.info('func1 %s'%args)
  16. time.sleep(5)
  17.  
  18. def func2(*args):
  19. logging.info('func2 %s' % args)
  20. time.sleep(5)
  21. def func3(*args):
  22. logging.info('func3 %s' % args)
  23. time.sleep(5)
  24.  
  25. def func4(*args):
  26. logging.info('func4 %s' % args)
  27. time.sleep(5)
  28.  
  29. def func5(*args):
  30. logging.info('func5 %s' % args)
  31. time.sleep(5)
  32.  
  33. newtimer.add([5,func1])
  34. newtimer.add([4,func2])
  35. newtimer.add([3,func3])
  36. newtimer.add([2,func4])
  37. newtimer.add([1,func5])
  38. time.sleep(1)
  39. newtimer.add([1,func1])
  40. newtimer.add([2,func2])
  41. newtimer.add([3,func3])
  42. newtimer.add([4,func4])
  43. newtimer.add([5,func5])
  44.  
  45. # 输出
  46. #
  47. # 07/27/2017 11:26:19 [Thursday] NewTimer wait
  48. # 07/27/2017 11:26:19 [Thursday] NewTimer add notify
  49. # 07/27/2017 11:26:19 [Thursday] NewTimer add notify
  50. # 07/27/2017 11:26:19 [Thursday] NewTimer add notify
  51. # 07/27/2017 11:26:19 [Thursday] NewTimer add notify
  52. # 07/27/2017 11:26:19 [Thursday] NewTimer add notify
  53. # 07/27/2017 11:26:19 [Thursday] NewTimer start sys timer and wait
  54. # 07/27/2017 11:26:20 [Thursday] NewTimer execute notify
  55. #
  56. # 07/27/2017 11:26:20 [Thursday] func5 1501125980.2175007
  57. # 07/27/2017 11:26:20 [Thursday] NewTimer add notify
  58. # 07/27/2017 11:26:20 [Thursday] NewTimer add notify
  59. # 07/27/2017 11:26:20 [Thursday] NewTimer add notify
  60. # 07/27/2017 11:26:20 [Thursday] NewTimer add notify
  61. # 07/27/2017 11:26:20 [Thursday] NewTimer add notify
  62. #
  63. #
  64. #
  65. #
  66. #
  67. # 07/27/2017 11:26:25 [Thursday] func4 1501125981.2175007
  68. #
  69. #
  70. #
  71. #
  72. # 07/27/2017 11:26:30 [Thursday] func1 1501125981.218279
  73. #
  74. #
  75. #
  76. #
  77. #
  78. #
  79. # 07/27/2017 11:26:35 [Thursday] func3 1501125982.2175007
  80. #
  81. #
  82. #
  83. #
  84. # 07/27/2017 11:26:40 [Thursday] func2 1501125982.218279
  85. #
  86. #
  87. #
  88. #
  89. #
  90. # 07/27/2017 11:26:45 [Thursday] func2 1501125983.2175007
  91. #
  92. #
  93. #
  94. #
  95. #
  96. # 07/27/2017 11:26:50 [Thursday] func3 1501125983.218279
  97. #
  98. #
  99. #
  100. #
  101. #
  102. # 07/27/2017 11:26:55 [Thursday] func1 1501125984.2175007
  103. #
  104. #
  105. #
  106. #
  107. #
  108. # 07/27/2017 11:27:00 [Thursday] func4 1501125984.218279
  109. #
  110. #
  111. #
  112. #
  113. #
  114. # 07/27/2017 11:27:05 [Thursday] func5 1501125985.218279
  115. #
  116. #
  117. #
  118. #
  119. #
  120. # 07/27/2017 11:27:10 [Thursday] NewTimer wait

输出

注:这次无论如何测试线程数也不会蹭蹭的上涨,同时可以实现多定时器任务要求;缺点:用到了两线程,没有用到单线程去实现,第二时间精准度问题,需要等待上个定时程序执行完毕,程序才能继续运行

  

Python开发【笔记】:单线程下执行多个定时任务的更多相关文章

  1. python开发笔记-通过xml快捷获取数据

    今天在做下python开发笔记之如何通过xml快捷获取数据,下面以调取nltk语料库为例: import nltk nltk.download() showing info https://raw.g ...

  2. Python 学习笔记(下)

    Python 学习笔记(下) 这份笔记是我在系统地学习python时记录的,它不能算是一份完整的参考,但里面大都是我觉得比较重要的地方. 目录 Python 学习笔记(下) 函数设计与使用 形参与实参 ...

  3. python开发笔记-python调用webservice接口

    环境描述: 操作系统版本: root@9deba54adab7:/# uname -a Linux 9deba54adab7 --generic #-Ubuntu SMP Thu Dec :: UTC ...

  4. python开发笔记-Python3.7+Django2.2 Docker镜像搭建

    目标镜像环境介绍: 操作系统:ubuntu16.04 python版本:python 3.7.4 django版本:2.2 操作步骤: 1.  本地安装docker环境(略)2. 拉取ubunut指定 ...

  5. Python开发:Windows下Python+Eclipse+Pydev开发环境配置

    一.配置前的准备: 1.安装jdk: 下载地址: https://www.oracle.com/technetwork/java/javase/downloads/index.html 2.安装Ecl ...

  6. python开发笔记之zip()函数用法详解

    今天分享一篇关于python下的zip()函数用法. zip()是Python的一个内建函数,它接受一系列可迭代的对象作为参数,将对象中对应的元素按顺序组合成一个tuple,每个tuple中包含的是原 ...

  7. Python 学习笔记21 CMD执行测试用例

    使用CMD命令执行测试用例 当我们在ride中设计好测试用例后,我们可以使用ride的界面工具来选择和运行测试用例. 系统也会提供比较好的报告和日志的浏览功能. 但是这样的自动化,毕竟是需要手工介入的 ...

  8. 【Python开发】Pycharm下的Anaconda配置

    我的系统是Win 64位的,用的Python 3.5.1 ,最近在学机器学习,用到了Numpy这个科学计算库,网上查了之后,看到很多装Numpy出问题的情况,所以决定装Anaconda,简单一些,并且 ...

  9. Python开发笔记之正则表达式的使用

    查找正则表达式 import re re_txt = re.compile(r'(\d)*.txt') m = re_txt.search(src) if not m == None: m.group ...

随机推荐

  1. WLAN高密无线网络部署的信道问题

    WIFI信号的信道有两部分,其中2.4G频段有13个左右交叠的信道(14信道只在日本使用),其中只能找出3个相互不重合的信道(具体请参考文末的链接),最常用的就是1.6.11这三个,当然也可以使用其他 ...

  2. git常用命令,助你快速入门

    git是程序员中最常用的版本控制工具,学会git的基本使用是十分重要,特别是在公司里面的协同开发,废话不多说,下面贴出常用的命令. 1.基本配置 # 既然git是多人协同的工具,别人要看到你提交代码的 ...

  3. qt中setStyleSheet导致的内存泄漏

    原始日期: 2017-01-05 19:31 现象:程序运行至某一个界面下,内存出现缓慢持续的占用内存增长   原因:经过排查,确定是在事件派发的槽函数中频繁重复调用setStyleSheet导致的 ...

  4. 修改phpstorm的字体样式和大小

    默认的字体实在太小,也太丑,必须修改下.就是强迫症,没错.下面截图配文字说明下 方法/步骤   首先进入设置,不解释   先设置软件界面上的字体.进入设置之后,选择(外观)Appearance.之后软 ...

  5. IP地址分类百科

    IP地址分类介绍 这里讨论IPv4,IP地址分成了A类.B类.C类.C类.E类,如下图所示: 解释: A类以0开头,网络地址有7位,主机地址有24位,举例:A类地址:0 10000000 000000 ...

  6. 淘宝tairKV分布式

    Tair是什么 Tair是由淘宝开发的key/value方案,系统默认支持基于内存和文件的存储引擎,对应于通常我们所说的缓存和持久化存储,这里可以获取更多关于tair的信息,淘宝团队介绍,Tair在淘 ...

  7. 一起学习c++11——c++11中的新增的容器

    c++11新增的容器1:array array最早是在boost中出现:http://www.boost.org/doc/libs/1_61_0/doc/html/array.html 当时的初衷是希 ...

  8. 日常API之百度翻译

    百度翻译是什么,可以吃吗?相信很多人都熟悉,它是我们生活中必不可少的一只东东. 但是,百度翻译开发平台只有每月只能翻译200万个字符,多出的要按照49.00/百万字符来算.对于我酱紫的乞丐程序员来说, ...

  9. 【Android Developers Training】 79. 连接到网络

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  10. 6.javaweb之respose对象

    1.respose的生成的outer对象要优先于内置的out对象输出 response.setContentType("text/html;charaset=utf-8");//设 ...