在Python中执行延迟操作的最有效方法

时间:2014-04-30 02:26:35

标签: python multithreading python-3.4

考虑一个系统,我有一些事件在不可预测的时间点进入。我希望能够在最后一个事件进入后执行一个“延迟”动作,该动作执行固定的时间量X单位。如果事件是最后一个发生的事件,则该事件被认为是“最后一个”。 X单位时间。在Python中执行此操作的最有效方法是什么?

我考虑过的一个解决方案是使用threading.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()

1 个答案:

答案 0 :(得分:1)

我之前做过类似的事情。

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非常慢。这是一个用Eventtime.sleep取代bool的解决方案。它还使用__slots__来加速属性访问

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)