【问题标题】:Python sleep without blocking other processesPython 睡眠而不阻塞其他进程
【发布时间】:2021-12-29 04:36:03
【问题描述】:

我每小时运行一个 python 脚本,并且我一直在 while 循环中使用time.sleep(3600)。它似乎可以根据需要工作,但我担心它会阻止新任务。我对此的研究似乎是它只阻塞了当前线程,但我想 100% 确定。虽然每小时的工作不应该超过 15 分钟,但如果它完成或挂起,我不希望它阻止下一个开始的工作。这就是我的做法:

import threading
import time


def long_hourly_job():
    # do some long task
    pass


if __name__ == "__main__":
    while True:
        thr = threading.Thread(target=long_hourly_job)
        thr.start()
        time.sleep(3600)

这就够了吗?

另外,我使用time.sleep 来完成这个每小时工作而不是 cron 工作的原因是我想在代码中做所有事情以使 dockerization 更干净。

【问题讨论】:

  • time.sleep 只阻塞当前线程。
  • @TimRoberts 感谢蒂姆。我就是这么想的,所以我现在的实现应该没问题吧?
  • @guy 是的,它应该每 3600 秒启动一次long_hourly_job。但如果您的 long_hourly_job 运行时间超过 1 小时,则可能存在重叠。
  • @niranjan94 只要没有被阻止,重叠就可以了,尽管你提出了一个很好的观点,可能会在一小时内杀死任何未完成的事情
  • 如果您想在容器中运行类似 crontab 的作业,我还建议您研究一下 apscheduler。我将它用于flask-app ML 再训练,它非常有效。 apscheduler.readthedocs.io/en/3.x/userguide.html

标签: python python-multithreading


【解决方案1】:

代码会起作用(即:sleep 只会阻塞调用线程),但您应该注意一些问题。其中一些已经在 cmets 中说明,例如线程之间时间重叠的可能性。主要问题是您的代码正在慢慢泄漏资源。创建线程后,即使线程运行完毕,操作系统也会保留一些数据结构。这是必要的,例如保持线程的退出状态直到线程的创建者需要它。清除这些结构(概念上相当于关闭文件)的函数称为join。已完成运行且不是joined 的线程称为“僵尸线程”。这些结构所需的内存量非常小,您的程序应该运行几个世纪以获得任何合理数量的可用 RAM。不过,加入您创建的所有线程是一个很好的做法。一个简单的方法(如果您知道 3600 秒足以让线程完成)是:

if __name__ == "__main__":
    while True:
        thr = threading.Thread(target=long_hourly_job)
        thr.start()
        thr.join(3600)  # wait at most 3600 s for the thread to finish
        if thr.isAlive(): # join does not return useful information
            print("Ooops: the last job did not finish on time")

如果您认为有时 3600 秒可能不足以让线程完成,这是一种更好的方法:

if __name__ == "__main__":
    previous = []
    while True:
        thr = threading.Thread(target=long_hourly_job)
        thr.start()
        previous.append(thr)
        time.sleep(3600)
        for i in reversed(range(len(previous))):
            t = previous[i]
            t.join(0)
            if t.isAlive():
                print("Ooops: thread still running")
            else:
                print("Thread finished")
                previous.remove(t)

我知道print 语句没有意义:请改用logging

【讨论】:

  • 感谢您的提示。我上周刚刚在考虑这个问题。我一直害怕原始线程永远不会真正死亡,只会永远消耗内存。这似乎解决了这个问题。
  • @guy 如果原始线程永远不会结束(即:一直在无限循环中运行或类似的情况),那么您无能为力,或者最后我不知道如何杀死一个线程。如果你想杀死一个没有按时完成的工作,你最好使用multiprocessing,但它本身也有一些问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 2011-02-25
  • 2021-05-01
相关资源
最近更新 更多