V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
sudoy
V2EX  ›  Python

Python 做一个闹钟,用 while 循环等待时间是否是一个好办法

  •  
  •   sudoy · 2020-10-16 10:57:13 +08:00 · 11057 次点击
    这是一个创建于 1503 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我在做一个闹钟,等到谋个时间就开始执行谋个动作(在下面的案例里面简化为 print('ok'))。请问这是否是最佳办法?因为我担心会不会导致占用内存过高等问题。我看有一个叫 alarm 的库,也是用 while 循环等待时间。以下是我的示例代码:

    在 2020 年 10 月 17 日 10 点 50 分 50 秒的时候触发事件 print('ok')

    from datetime import datetime
    
    def alarm():
    	while True:
    		now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    		if now == '2020-10-17 10:50:50':
    			print('ok')
    			break
    
    
    if __name__ == '__main__':
    	alarm()
    
    第 1 条附言  ·  2020-10-17 10:49:52 +08:00

    感谢大家的回复!根据大家的建议和帮助,我决定采用计算时间差然后用sleep的办法。其他办法比如用第三方库或者线程等方法也很好,但我希望尽量简单点。这个只需要精确到未来某个时间的秒即可。我更新代码如下,希望对其他新手有帮助:

    from time import sleep
    from datetime import datetime
    
    def alarm():
    	now = datetime.now().timestamp()
    	set_time = datetime(2020, 10, 16, 22, 42, 50).timestamp()
    	delta = set_time - now
    	sleep(delta)
    	print('ok')
    
    
    if __name__ == '__main__':
    	alarm()
    
    
    108 条回复    2020-10-19 11:38:53 +08:00
    1  2  
    duyuyouci
        1
    duyuyouci  
       2020-10-16 11:20:41 +08:00
    可以用 celery 做定时任务啊
    ruanimal
        2
    ruanimal  
       2020-10-16 11:25:12 +08:00   ❤️ 3
    循环里加个 sleep 吧,不然 cpu 都占满了
    nevin47
        3
    nevin47  
       2020-10-16 11:25:52 +08:00
    CPU 都炸了。。。。。。
    RRRoger
        4
    RRRoger  
       2020-10-16 11:26:23 +08:00
    用操作系统的定时任务触发是不是会更好点
    vZexc0m
        5
    vZexc0m  
       2020-10-16 11:26:49 +08:00   ❤️ 4
    可以用 APScheduler
    yaoye555
        6
    yaoye555  
       2020-10-16 11:27:59 +08:00   ❤️ 1
    crontab 或许更值得拥有
    qile1
        7
    qile1  
       2020-10-16 11:29:44 +08:00 via Android
    这样应该可以,但是有时候 print 有数据,程序确不执行挺奇怪,好像也没有啥好办法
    ysc3839
        8
    ysc3839  
       2020-10-16 11:31:12 +08:00   ❤️ 1
    内存占用不高,CPU 使用率会高。如果不想使用操作系统独有的 API 的话,那就加上 sleep 吧。
    no1xsyzy
        9
    no1xsyzy  
       2020-10-16 11:31:46 +08:00
    占满一个核,内存不会泄漏
    不要用等于判断时间
    stephenxiaxy
        10
    stephenxiaxy  
       2020-10-16 11:32:09 +08:00
    。。。
    MoYi123
        11
    MoYi123  
       2020-10-16 11:50:30 +08:00   ❤️ 1
    不如 asyncio.sleep
    MagnifierSun
        12
    MagnifierSun  
       2020-10-16 12:56:12 +08:00   ❤️ 1
    ??? 你知道这个语句一秒钟要执行多少遍吗哈哈哈
    有个 Schedule 的库,自己去搜搜
    Chenamy2017
        13
    Chenamy2017  
       2020-10-16 13:25:49 +08:00   ❤️ 2
    加 sleep 即可,时间也不要用==,超过这个时间后就执行动作。
    Sapp
        14
    Sapp  
       2020-10-16 13:34:45 +08:00
    你这个时间必出坑...
    kimqcn
        15
    kimqcn  
       2020-10-16 13:37:19 +08:00
    谁把我的代码偷出来了呵呵呵
    Vhc001
        16
    Vhc001  
       2020-10-16 13:37:32 +08:00   ❤️ 6
    这是我见过最坑爹的代码
    Chaidu
        17
    Chaidu  
       2020-10-16 13:39:48 +08:00   ❤️ 1
    建议转行 (狗头
    Tokiomi
        18
    Tokiomi  
       2020-10-16 13:43:55 +08:00
    哈哈哈,学生代码吧
    sudoy
        19
    sudoy  
    OP
       2020-10-16 13:44:50 +08:00
    谢谢大家回复,看来是用 sleep 比较合适。 @duyuyouci celery 可能比较重型了,我想尽量简单点,轻量级的。不过感谢推荐。 @vZexc0m APScheduler 看起来不错,不过还没有用过这个库,回头研究一下
    est
        20
    est  
       2020-10-16 13:46:06 +08:00   ❤️ 1
    其实底层库就是你这样实现的。只不过加了很多东西。比如上面说的要加个 sleep(1) 这就是你最小定时精度。
    sudoy
        21
    sudoy  
    OP
       2020-10-16 13:52:06 +08:00
    @Sapp @kimqcn 所以来请教你们啊 :)
    sudoy
        22
    sudoy  
    OP
       2020-10-16 13:54:35 +08:00
    @Chaidu 本来就不是程序猿 哈哈哈,业余爱好者😁🤣
    widewing
        23
    widewing  
       2020-10-16 13:59:21 +08:00 via Android
    sleep 为什么要加在循环里?直接 sleep 指定时间不香吗?
    dswyzx
        24
    dswyzx  
       2020-10-16 14:02:06 +08:00 via iPhone
    别听他们的,加 sleep 万一错过这个点了呢,大于等于时间万一没有拯救世界成功呢。
    综上所述:能用就行!
    imdong
        25
    imdong  
       2020-10-16 14:03:15 +08:00   ❤️ 1
    之前有过类似的需求,不过我的做法是用 sleep 但是不是 sleep(1)
    而是再执行完毕后,检查距离下一次执行的时间差,然后睡眠到那个时间。
    shuax
        26
    shuax  
       2020-10-16 14:04:56 +08:00
    sleep(0)即可
    est
        27
    est  
       2020-10-16 14:21:49 +08:00
    @widewing 设想一个场景。你在虚拟机里跑起来了这个程序。你让虚拟机暂停了。然后 1 个小时后,恢复了虚拟机。这个时候你的 sleep 还是对的吗。
    sudoy
        28
    sudoy  
    OP
       2020-10-16 14:21:56 +08:00
    @est 哈哈,虽然我找半天没有找到 python 内置的 `sleep()` 是怎么写的,你的话听起来还挺鼓励人的。不过他们说这样写会导致 CUP 占用过高,可能确实没有用 sleep 好,sleep 没有这么高的 CUP 占用。
    wusheng0
        29
    wusheng0  
       2020-10-16 14:24:38 +08:00 via Android
    是不是 fork 一个进程后台运行更好,
    有没有大佬来说一下
    felixcode
        30
    felixcode  
       2020-10-16 14:27:06 +08:00
    多开几个闹钟就能把多核 CPU 占满了。
    sudoy
        31
    sudoy  
    OP
       2020-10-16 14:44:57 +08:00
    @shuax sleep(0) 也是解决不了 CUP 占用过高的问题的
    ![]( )
    zkqiang
        32
    zkqiang  
       2020-10-16 14:48:36 +08:00
    没让你 sleep 0 啊。。直接计算 定时减去现在 有多少秒,sleep 多少秒就行了
    ljsh093
        33
    ljsh093  
       2020-10-16 14:55:47 +08:00 via iPhone
    @imdong 设定好 sleep 以后不就没法取消了?只能从外部打断重新设置时间?
    zkqiang
        34
    zkqiang  
       2020-10-16 14:56:36 +08:00   ❤️ 1
    或者 sleep(0.5) 每半秒检查一次,另外时间不要用 == 判断,最好用区间判断允许一些误差
    imdong
        35
    imdong  
       2020-10-16 15:00:47 +08:00
    @ljsh093 没有取消的需求,如果有,就在执行前做个判断,一样。

    但是会有类似楼主说的睡眠唤醒的问题。
    bruce00
        36
    bruce00  
       2020-10-16 15:06:42 +08:00
    狗头真的能保命
    heyjei
        37
    heyjei  
       2020-10-16 15:09:29 +08:00
    我真的在生产环境见过这样的代码,你们应该能够理解我当时的心情。
    user8341
        38
    user8341  
       2020-10-16 15:13:02 +08:00   ❤️ 2
    sleep(0)真实绝了。
    jerfoxu
        39
    jerfoxu  
       2020-10-16 15:17:18 +08:00
    @imdong 这个方法可以哟!
    Norie
        40
    Norie  
       2020-10-16 15:17:51 +08:00 via iPhone
    这是 CPU 性能测试
    fuchunliu
        41
    fuchunliu  
       2020-10-16 15:29:58 +08:00 via Android
    你是对的,他要听他们的,不然别人说你跑的假程序,CPU 都没占用
    ysc3839
        42
    ysc3839  
       2020-10-16 15:37:40 +08:00   ❤️ 1
    @est Windows 有 SetThreadpoolTimer,这个可以设置绝对时间,不受睡眠影响,还可以设置允许系统推迟执行,相比 sleep 更加省电。
    winglight2016
        43
    winglight2016  
       2020-10-16 15:38:58 +08:00
    @heyjei 这样的生产环境还能有任何“生产”吗?🐶
    Bijiabo
        44
    Bijiabo  
       2020-10-16 15:40:28 +08:00
    看到 sleep(0) 笑死我了哈哈哈
    lazyfighter
        45
    lazyfighter  
       2020-10-16 15:40:29 +08:00
    既然 sleep,为什么不算一下 sleep 多长时间呢
    ThisQ
        46
    ThisQ  
       2020-10-16 15:41:06 +08:00
    用系统定时任务调用脚本执行呢?
    ysc3839
        47
    ysc3839  
       2020-10-16 15:45:23 +08:00   ❤️ 1
    @wusheng0 我一般会选择用新线程来后台执行,fork 在某些操作系统上有坑,比如 Windows 没有 fork,macOS fork 之后不 exec 没法使用 CoreFoundation 。用线程的话可以共享资源,也没有那么多坑。
    zgzb
        48
    zgzb  
       2020-10-16 15:47:19 +08:00 via Android
    我选择 after
    nine9
        49
    nine9  
       2020-10-16 15:50:45 +08:00   ❤️ 1
    可以考虑 threading.Event()

    event.wait(timeout=None)
    event.set()

    也就是 APScheduler 这个库实现使用的
    est
        50
    est  
       2020-10-16 15:54:53 +08:00
    @ysc3839 这个就是 OS 帮你做了很多脏活。。。
    0bit
        51
    0bit  
       2020-10-16 16:01:03 +08:00   ❤️ 1
    sleep 的问题已经很多人说了,我提一下关于时间比对的问题吧。
    现在这样用 str 比对,容易有小坑,还是应该用更本质的数据去比对,比如 unix timestamp,更不容易出错。
    nuk
        52
    nuk  
       2020-10-16 16:22:49 +08:00
    最好调整成实时优先级,不然这一秒容易被跳过
    findlisa
        53
    findlisa  
       2020-10-16 16:23:47 +08:00 via Android
    @ruanimal 为啥,一个循环就能把 cpu 占满吗
    DoctorCat
        54
    DoctorCat  
       2020-10-16 16:33:38 +08:00   ❤️ 1
    兼容 posix 标准的系统还可以利用系统信号:signal 、setitimer 定时器交给 OS 去做了
    ma7x
        55
    ma7x  
       2020-10-16 16:34:24 +08:00
    win10 自带闹钟超级好用 为什么要造轮子
    www5070504
        56
    www5070504  
       2020-10-16 16:34:50 +08:00
    你这种写法 每天多费半度电吧 加个 timer 之类的 sleep 都行啊
    SmartKeyerror
        57
    SmartKeyerror  
       2020-10-16 16:34:56 +08:00
    单线程直接用信号不就好了,向内核注册一个定时器,定时器到期后向进程发送 SIGALRM 信号。
    darknoll
        58
    darknoll  
       2020-10-16 16:37:27 +08:00
    传说中的忙等待自旋锁?
    fish267
        59
    fish267  
       2020-10-16 16:40:03 +08:00
    还是 crontab 吧
    muzuiget
        60
    muzuiget  
       2020-10-16 16:43:57 +08:00
    哈哈,我也想到传说中的自旋锁。
    xionger
        61
    xionger  
       2020-10-16 16:45:36 +08:00
    哈哈 劝改行
    ruanimal
        62
    ruanimal  
       2020-10-16 16:51:21 +08:00
    @findlisa 每时每刻都在判断时间是否相等,也就是 cpu 一直是忙碌的状态,自然就会占满一个核心。如果有 sleep,cpu 在你程序 sleep 的期间就干别的,或者休息去了。

    ps: 去看看操作系统的书,看看进程的调度。
    GoLand
        63
    GoLand  
       2020-10-16 16:52:47 +08:00
    淦,这么精妙的代码我怎么想不到。
    shuax
        64
    shuax  
       2020-10-16 16:54:46 +08:00
    哎哟,我错了,sleep 0 确实不行,起码得 0.001
    kiracyan
        65
    kiracyan  
       2020-10-16 16:58:28 +08:00
    硬是要这么做建议你 sleep 个 300 秒 然后判断一下当前时间距离设定时间是不是小于 300 秒 再 sleep 1 秒
    shenqi
        66
    shenqi  
       2020-10-16 17:04:06 +08:00
    。。。明显的实习生写的代码。

    (又不是不能用.jpg )
    Hxu2M811KVSJqN75
        67
    Hxu2M811KVSJqN75  
       2020-10-16 17:06:43 +08:00
    这个算今日最佳 [欢乐贴] 么?
    jimmyismagic
        68
    jimmyismagic  
       2020-10-16 17:12:19 +08:00   ❤️ 1
    评论里说实习生写的代码的,自己可以写一个出来试试,看能做到多高的精度,要做到等于而不是大于等于
    caiji11
        69
    caiji11  
       2020-10-16 17:16:01 +08:00
    @xionger 头像可爱 大熊 这个哪里不太好
    sapocaly
        70
    sapocaly  
       2020-10-16 17:21:45 +08:00
    1.https://docs.python.org/3.7/library/sched.html
    2.就是想写代码:用 sleep 或用 os api
    3.用现有 pkg
    iSecret
        71
    iSecret  
       2020-10-16 17:36:17 +08:00
    用 crontab 执行任务的话没这么多蛋疼的事还可以顺道解决了可能需要重复执行的问题,非要用 py 建议 sleep(执行时间 - 当前时间)。
    tabris17
        72
    tabris17  
       2020-10-16 17:42:57 +08:00
    实现了一个野生 spinning
    lane1
        73
    lane1  
       2020-10-16 17:53:00 +08:00
    用术语来说, 你的写法是 spinlock, busy-waiting, CPU-bound... 不过话说计时器到底如何实现呢?
    Tonni
        74
    Tonni  
       2020-10-16 17:58:12 +08:00   ❤️ 1
    哈哈,别的不说,楼主头像的狗子好可爱🐶
    UN2758
        75
    UN2758  
       2020-10-16 18:31:52 +08:00   ❤️ 1
    lz 这么狂野的写法,怀念当年的我,哈哈哈哈
    JCZ2MkKb5S8ZX9pq
        76
    JCZ2MkKb5S8ZX9pq  
       2020-10-16 18:43:57 +08:00
    直接算 timestamp 的时间差,sleep 一次到底不就好了。
    这个格式化时间的操作有点多余,重复计算没必要啊。
    wuwukai007
        77
    wuwukai007  
       2020-10-16 18:52:31 +08:00 via Android
    直接计算时间差,sleep,每次激活的时候重新计算,
    lysS
        78
    lysS  
       2020-10-16 18:56:57 +08:00
    想到见过 JS 延时函数:
            function sleepSync(ms) {
                var curr = new Date().getTime();
                ms += curr;
                while (curr < ms) {
                    curr = new Date().getTime();
                }
            }
    CSM
        79
    CSM  
       2020-10-16 19:10:16 +08:00 via Android
    直接一觉睡到目标时间,醒来后完成任务就行,循环也不用
    echoick
        80
    echoick  
       2020-10-16 19:17:21 +08:00 via iPhone
    哈哈哈哈 sleep(0)
    inframe
        81
    inframe  
       2020-10-16 20:08:47 +08:00
    我真的在生产环境见过这样的代码,你们应该能够理解我当时的心情。

    +1
    ctro15547
        82
    ctro15547  
       2020-10-16 20:46:06 +08:00
    延迟 0.1 秒就行 。。
    E1n
        83
    E1n  
       2020-10-16 23:37:42 +08:00 via Android
    哈哈加油
    Yinz
        84
    Yinz  
       2020-10-17 00:05:27 +08:00
    不得不说,楼主心态蛮好的,加油,谁都有新手阶段,学习即可
    by73
        85
    by73  
       2020-10-17 00:11:30 +08:00
    突然对 time.sleep 有了点兴趣,最后发现居然调用的是 select() 系统调用😂
    irytu
        86
    irytu  
       2020-10-17 00:19:26 +08:00
    os.sched_yield()

    不过 Windows 上没有这种方法 我一般为了 portable 会写成 time.sleep(0.0001)
    vcfghtyjc
        87
    vcfghtyjc  
       2020-10-17 00:25:28 +08:00   ❤️ 1
    运行时候检测一下当前时间,然后算出需要的睡眠时间,sleep 就行了
    shijingshijing
        88
    shijingshijing  
       2020-10-17 00:42:59 +08:00
    sleep(0)没毛病,代码不改,在 crontab 里面设定 2020 年 10 月 17 日 10 点 50 分 50 秒执行一次就 ok 了。
    discrete
        89
    discrete  
       2020-10-17 06:22:20 +08:00
    想起了 Sleep Sort.
    sudoy
        90
    sudoy  
    OP
       2020-10-17 09:51:03 +08:00
    @nuk 谢谢!请问具体怎么操作?
    sudoy
        91
    sudoy  
    OP
       2020-10-17 09:53:01 +08:00
    @ma7x 这个问题问的好。当然是定时调用 API 啦,Windows 10 自带的闹钟并非可编程的
    inorilzy
        92
    inorilzy  
       2020-10-17 10:03:30 +08:00
    APScheduler 和 celery 都可以定时任务。
    findlisa
        93
    findlisa  
       2020-10-17 10:04:46 +08:00 via Android
    @ruanimal 好的👌
    raymanr
        94
    raymanr  
       2020-10-17 13:08:19 +08:00
    @sudoy 如果纯粹是为了定时的话, 就 windows 的任务计划就挺好, 而且足够可靠, 自己写会有很多坑
    sudoy
        95
    sudoy  
    OP
       2020-10-17 14:13:16 +08:00 via iPhone
    @raymanr 谢谢
    nuistzhou
        96
    nuistzhou  
       2020-10-17 14:57:37 +08:00 via iPhone
    为何我想起了大名鼎鼎的“睡眠排序法”?!
    sudoy
        97
    sudoy  
    OP
       2020-10-17 15:32:01 +08:00 via iPhone
    @nuistzhou 就是那个从天而降的掌法吗?
    adamwong
        98
    adamwong  
       2020-10-17 15:52:36 +08:00
    @SmartKeyerror 我怎么又抓到你了
    nuk
        99
    nuk  
       2020-10-17 17:18:14 +08:00   ❤️ 1
    @sudoy
    import win32api,win32process,win32con

    pid = win32api.GetCurrentProcessId()
    handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
    win32process.SetPriorityClass(handle, win32process.REALTIME_PRIORITY_CLASS)
    IDAEngine
        100
    IDAEngine  
       2020-10-17 18:48:11 +08:00 via iPhone
    用系统 API 不香吗? while sleep 不靠谱
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1067 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:36 · PVG 05:36 · LAX 13:36 · JFK 16:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.