在Python中执行延迟操作的最有效方法
考虑一个系统,在这个系统中,事件发生在不可预测的时间点。我希望能够执行一个“延迟”操作,在最后一个事件发生后执行一个固定的时间量X单位。如果事件是在最后X个时间单位内发生的唯一事件,则该事件被视为“最后一次”。在Python中最有效的方法是什么 我考虑过的一个解决方案是使用在Python中执行延迟操作的最有效方法,python,multithreading,python-3.4,Python,Multithreading,Python 3.4,考虑一个系统,在这个系统中,事件发生在不可预测的时间点。我希望能够执行一个“延迟”操作,在最后一个事件发生后执行一个固定的时间量X单位。如果事件是在最后X个时间单位内发生的唯一事件,则该事件被视为“最后一次”。在Python中最有效的方法是什么 我考虑过的一个解决方案是使用线程。Event: # This solution has the drawback that the deferred event may actually occur # up to 2*X units of time a
线程。Event
:
# This solution has the drawback that the deferred event may actually occur
# up to 2*X units of time after the last event.
# Also, it kinda sucks that the thread is basically polling once the first
# event comes in.
from threading import Thread
from threading import Event
import time
import sys
evt = Event()
die = False
X = 1
def thread_func_event():
while True:
evt.wait()
if die:
break
while True:
evt.clear()
time.sleep(X)
if not evt.is_set():
# No more events came in. Good.
break
# Looks like more events came in. Let's try again.
if die:
return
print('Deferred action performed.')
sys.stdout.flush()
def event_occurred():
evt.set()
t = Thread(target=thread_func_event)
t.start()
for _ in range(0, 1000000):
event_occurred()
print('First batch of events done.')
sys.stdout.flush()
time.sleep(3)
for _ in range(0, 1000000):
event_occurred()
print('Second batch of events done.')
sys.stdout.flush()
time.sleep(3)
die = True
evt.set()
t.join()
我以前做过类似的事情
import threading
import time
class waiter(object):
def __init__(self, action, delay = 0.5, *args, **kwargs):
self.action_lockout_timeout = threading.Thread()
self.action_lockout_event = threading.Event()
self.action = action
self.delay = delay
self.action_prevent()
def action_prevent(self):
def action_enable():
self.action_lockout_event.wait(self.delay)
if not self.action_lockout_event._Event__flag:
self.action()
if self.action_lockout_timeout.isAlive():
self.action_lockout_event.set()
self.action_lockout_timeout.join()
self.action_lockout_event.clear()
self.action_lockout_timeout = threading.Thread(target = action_enable)
self.action_lockout_timeout.setDaemon(True)
self.action_lockout_timeout.start()
def thanks():
print("Person 2: Thank you ...")
polite = waiter(thanks, 3)
print("Person 1: After you")
polite.action_prevent()
time.sleep(2)
print("Person 2: No, after you")
polite.action_prevent()
time.sleep(2)
print("Person 1: No I insist")
polite.action_prevent()
time.sleep(2)
print("Person 2: But it would be rude")
polite.action_prevent()
time.sleep(2)
print("---Akward Silence---")
time.sleep(2)
如果要运行带参数的函数,只需使用lambda
表达式将其包装即可
def thanks(person):
print("%s: Thank you ..." % person)
polite = waiter(lambda: thanks("Person 2"), 3)
编辑:
原来threading.Event
相当慢。这里有一个解决方案,它将事件
替换为time.sleep
和bool
。它还使用\uuuuuuuuuuuuuuuuuuu
来加速属性访问
import sys
import threading
import time
class waiter(object):
__slots__ = \
[
"action",
"delay",
"undelayed",
"delay_timeout",
]
def __init__(self, action, delay = 0.5, *args, **kwargs):
self.action = action
self.delay = delay
self.undelayed = False
self.delay_timeout = threading.Thread(target = self.action_enable)
self.delay_timeout.start()
def action_prevent(self):
self.undelayed = False
def action_enable(self):
while True:
time.sleep(self.delay)
if self.undelayed:
self.action()
break
else:
self.undelayed = True
def thanks():
print("Person 2: Thank you ...")
polite = waiter(thanks, 1)
for _ in range(0, 1000000):
polite.action_prevent()
print("First batch of events done.")
time.sleep(2)
这似乎没有那么有效,因为每次“中断”时,都会破坏当前线程,并创建一个新线程。是的,这有助于满足延迟时间保证,但我宁愿牺牲一些时间保证来获得更好的性能。考虑一下这个解决方案的性能,我在上面的代码中举例说明,其中有1000000个中断。好多了,但基本上,你似乎正在接近我的解决方案。您可以进行的下一个优化是有一个长时间运行的线程,此时您需要某种方式来“通知”线程。正如你所看到的,我用了一个事件。我从您的解决方案中得到的一个想法是,如果主线程能够以某种方式确定辅助线程是否仍在运行和循环,我可以避免调用Event.set,而是设置bool或其他指示中断的内容,我认为这样会更便宜。让我再考虑一下,是的,我建议避免
threading.Event
,因为它很慢。