Python,threading.timer 对象不会运行函数定时器?

Posted

技术标签:

【中文标题】Python,threading.timer 对象不会运行函数定时器?【英文标题】:Python, threading.timer object will not run function timer? 【发布时间】:2013-06-22 14:17:42 【问题描述】:

我正在尝试运行延迟发送电子邮件,因为发送电子邮件的条件可能会开启很长一段时间,而且我不想接收和无限量的电子邮件警报...

为此,我正在尝试使用 threading.timer 进行延迟,并且仅每 15 分钟发送一次电子邮件...我尝试了 .timer 对象的 900 秒长延迟,它可以工作(使用时间脚本)。 ..但是当我运行它来发送电子邮件时,它首先发送电子邮件,然后进入计时器而不运行脚本的其他部分......电子邮件功能工作得很好......运行python 2.6.6

#!/usr/bin/env python

import time
import smtplib #for sending emails when warning
import threading

if True: #the possibility exists that the conditional is met several times thus sending lots of emails
    t = threading.Timer(300,send_email('Tank temperature Overheat',tank_temp))
    t.start() # after 300 seconds, the email will be sent but the script will keep running
print "rest of the script keeps running"
print "keeps running the scrpit and after 300s the email is sent"

关于为什么它不起作用或其他解决方法的任何想法?

玩过之后……它会进入睡眠状态,但会发送所有电子邮件……不是每 X 时间设置的一封电子邮件……即

n=300

start = time.time()

while (time.time() - start < n):

    led_temp = 56

        if led_temp > 55:
        t = threading.Timer(100, lambda: send_email('Lights temperature Overheat',led_temp))
        t.start()

我不是每 100 秒收到一封电子邮件,而是在 300 秒后收到 36 封电子邮件 .. ??知道为什么吗? (根据下面的评论重新格式化)

在阅读了关于线程的答案后,我理解了这个问题......我仍然知道 python 并且从未使用过用户线程,所以我想这是我在创建无数线程时收到的 36 封电子邮件的根本原因......我通过使用标志来修复它,并像这样测试代码:

def raise_flag():
    global start
    interval = 300
    if start > interval:
        start = 0
        flag = True
        print "Flag True, sending email"
        return flag
    else:
        flag = False
        start = start + 1
        print "Flag OFF", start
        time.sleep(1)
        return flag

led_temp = 27
while led_temp > 26:
    flag = raise_flag()
    if flag:
        send_email('Tank temperature Overheat',led_temp)
        flag = False
        print "Sent email"

【问题讨论】:

【参考方案1】:

作为timer 的第二个参数,您应该传递一个可调用对象,但在您的情况下,您正在调用该函数并将结果传递给threading.Timer。您应该改用 lambda 表达式:

#!/usr/bin/env python

import time
import smtplib
import threading

if True:
    t = threading.Timer(300, lambda: send_email('Tank temperature Overheat',tank_temp))
    t.start() 
print "rest of the script keeps running"
print "keeps running the scrpit and after 300s the email is sent"

表达式:

lambda: send_email('Tank temperature Overheat',tank_temp)

评估一个不带参数的函数,调用时会使用该参数执行send_email,而在您的代码中您有:

t = threading.Timer(300,send_email('Tank temperature Overheat',tank_temp))

这将首先评估所有参数,然后调用send_email,然后然后创建Timer 对象。


关于 300 秒内 36 封电子邮件的问题,在您的代码中:

n=300

start = time.time()

while (time.time() - start < n):

    led_temp = 56

        if led_temp > 55:
        t = threading.Timer(100, lambda: send_email('Lights temperature Overheat',led_temp))
        t.start()

while 将在 300 秒的迭代中创建大量线程。我不知道你为什么认为这个循环应该每 100 秒向你发送一封电子邮件。每个线程会在 100 秒后向您发送一封电子邮件,但线程数很多。

如果您只想发送 3 封电子邮件,那么您应该在 3 次迭代后将break 退出循环。此外,迭代可能太快了,因此所有计时器几乎会同时发送所有电子邮件,因为它们的超时时间几乎相等。

你可以通过以下代码看到这个问题:

>>> import threading
>>> import threading
>>> def send_email(): print("email sent!")
... 
>>> for _ in range(5):
...     t = threading.Timer(7, send_email)
...     t.start()
... 
>>> email sent!
email sent!
email sent!
email sent!
email sent!

尽管创建了 5 个不同的 Timers,但它们几乎同时超时,因此您会看到所有 email sent! 同时出现。您可以修改超时以考虑到这一点:

>>> for i in range(5):
...     t = threading.Timer(7 + i, send_email)
...     t.start()
... 
>>> email sent!
email sent!
email sent!
email sent!
email sent!

在上面的代码中,您会看到email sent! 一次出现一个,间隔大约为 1 秒。

最后我要补充一点,您无法控制何时收到电子邮件。这由不同的服务以不同的方式处理,因此无法保证在运行您的代码时,收件人将每 100 秒收到一封电子邮件。

【讨论】:

就像一个魅力.. 我还是 python 新手,还没有见过 lambda 函数.. 我会做一些阅读 玩过之后...它会进入睡眠状态,但会发送所有电子邮件...不是每设置 X 个 .time 时间发送一封电子邮件...即 'n=300 start = time。 time() while (time.time() - start 55: t = threading.Timer(100, lambda: send_email('Lights temperature Overheat',led_temp)) t.start() ' 如果我运行此代码,我会模拟临时警告高于该值 300 秒。我不是每 100 秒收到一封电子邮件,而是在 300 秒后收到 36 封电子邮件.. ??知道为什么吗? @user2520982 您可以编辑您的问题并详细说明现在的问题吗? cmets 中的代码根本不可读。 AFAIK 电子邮件不保证是即时的,因此提供商等可能一次发送多于一封电子邮件。 我在问题中添加了评论...任何输入?【参考方案2】:

threading.Timer 可以在两种模式下运行:

第一种选择是使用方法提供的参数:

t = threading.Timer(interval=300, function=send_email, args=['Tank temperature Overheat', tank_temp])
t.start()

第二种选择是使用“lambda 函数”

t = threading.Timer(interval=300, lambda: send_email('Tank temperature Overheat', tank_temp))
t.start()

【讨论】:

【参考方案3】:

不确定这是否是您要查找的内容,但如果您的问题是计时器线程独立于主脚本运行,您可以像这样join 该线程:

t.start()
t.join() # will wait until the thread `t` has finished

但是这种方式违背了在单独线程中运行函数的目的。因此,您可能希望在发送该电子邮件之前或之后只 time.sleep 一段时间。

顺便说一句,请确保对send_email('Tank temperature Overheat',tank_temp) 的调用实际上返回了一个函数,该函数将在定时器线程启动时调用并且不发送实际邮件。

【讨论】:

以上是关于Python,threading.timer 对象不会运行函数定时器?的主要内容,如果未能解决你的问题,请参考以下文章

Python threading 单线程 timer重复调用函数

python threading.timer怎么就收返回值

Python 之定时器

如何处理重复 System.Threading.Timer [重复]

Python3.x:简单时间调度Timer(间隔时间执行)

C# 中的 System.Threading.Timer 似乎无法正常工作。它每 3 秒运行一次非常快