2

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

这是不起作用的:

if user_behaving_badly():
  ban( user )
  time.sleep( penalty_duration )  # Bad! Blocks the entire application!
  unban( user )

理想情况下,如果 user_behave_badly() 为真,我想启动一个新线程,它只会禁止用户,然后休眠一段时间,取消禁止用户,然后线程消失。

据此,我可以使用以下方法实现我的目标:

if user_behaving_badly():
  thread.start_new_thread( banSleepUnban, ( user, penalty ) )

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

4

5 回答 5

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()

并像这样使用:

B = Bans()
B.ban_user("phil", 30.0)
B.is_user_banned("phil")
于 2011-10-15T02:52:13.557 回答
3

为什么要穿线?

do_something(user):
  if(good_user(user)):
    # do it
  else
    # don't

good_user():
  if(is_user_baned(user)):
    if(past_time_since_ban(user)):
      user_good_user(user)
  elif(is_user_bad()):
    ban_user()

ban_user(user):
  # add a user/start time to a hash

is_user_banned()
  # check hash
  # could check if expired now too, or do it seperately if you care about it

is_user_bad()
  # check params or set more values in a hash
于 2011-10-15T02:22:02.953 回答
3

使用线程计时器对象,如下所示:

t = threading.Timer(30.0, unban)
t.start() # after 30 seconds, unban will be run

然后只在线程中运行 unban。

于 2011-10-15T02:29:07.823 回答
0

这与语言无关,但考虑使用线程来跟踪内容。该线程在表中保留了一个数据结构,其中包含“username”和“banned_until”之类的内容。线程始终在后台运行检查表,如果banned_until 过期,它会解除对用户的阻止。其他线程正常进行。

于 2011-10-15T02:11:22.317 回答
0

如果您使用的是 GUI,
大多数 GUI 模块都有一个计时器功能,它可以抽象所有令人讨厌的多线程内容,并在给定时间后执行代码,但仍然允许执行其余代码。

例如,Tkinter 有 'after' 功能。

于 2011-10-15T02:26:20.030 回答