运行Python延迟循环的最痛苦的方法

Ste*_* V. 2 python multithreading python-multithreading

我有一个事件驱动的聊天机器人,我正在尝试实施垃圾邮件防护.我想让一段时间内表现不佳的用户沉默,而不会阻塞应用程序的其余部分.

这是不起作用的:

if user_behaving_badly():
  ban( user )
  time.sleep( penalty_duration )  # Bad! Blocks the entire application!
  unban( user )
Run Code Online (Sandbox Code Playgroud)

理想情况下,如果user_behaving_badly()为true,我想启动一个新的线程,除了禁止用户之外什么都不做,然后睡一会儿,解开用户,然后线程消失.

根据这个,我可以使用以下内容完成我的目标:

if user_behaving_badly():
  thread.start_new_thread( banSleepUnban, ( user, penalty ) )
Run Code Online (Sandbox Code Playgroud)

"简单"通常是"好"的指标,这很简单,但我听到的关于线程的一切都说他们会以意想不到的方式咬你.我的问题是:有没有比这更好的方法来运行一个简单的延迟循环而不阻塞应用程序的其余部分?

Dan*_* D. 5

而不是为每个禁令启动一个线程,将禁令放在优先级队列中,并让一个线程执行休眠和取消禁止

这段代码使两个结构成为一个heapq,它允许它快速找到最快的禁用期限和一个dict,以便快速检查用户是否被名称禁止

import time
import threading
import heapq

class Bans():
    def __init__(self):
        self.lock = threading.Lock()
        self.event = threading.Event()
        self.heap = []
        self.dict = {}
        self.thread = threading.thread(target=self.expiration_thread)
        self.thread.setDaemon(True)
        self.thread.start()

    def ban_user(self, name, duration):
        with self.lock:
            now = time.time()
            expiration = (now+duration) 
            heapq.heappush(self.heap, (expiration, user))
            self.dict[user] = expiration
            self.event.set()

    def is_user_banned(self, user):
        with self.lock:
            now = time.time()
            return self.dict.get(user, None) > now

    def expiration_thread(self):
        while True:
            self.event.wait()
            with self.lock:
                next, user = self.heap[0]
                now = time.time()
                duration = next-now
            if duration > 0:
                time.sleep(duration)
            with self.lock:
                if self.heap[0][0] = next:
                    heapq.heappop(self.heap)
                    del self.dict(user)
                if not self.heap:
                    self.event.clear()
Run Code Online (Sandbox Code Playgroud)

并像这样使用:

B = Bans()
B.ban_user("phil", 30.0)
B.is_user_banned("phil")
Run Code Online (Sandbox Code Playgroud)