Наименее болезненный способ запустить цикл задержки Python - PullRequest
2 голосов
/ 15 октября 2011

У меня есть чат-робот, управляемый событиями, и я пытаюсь реализовать защиту от спама. Я хочу заставить пользователя замолчать, который некоторое время ведет себя плохо, не блокируя остальную часть приложения.

Вот что не работает:

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

В идеале, если user_behaving_badly () имеет значение true, я хочу запустить новый поток, который ничего не делает, кроме как заблокировать пользователя, затем ненадолго спать, разблокировать пользователя, а затем поток исчезает.

В соответствии с этим я могу достичь своей цели, используя следующее:

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

«Простой» обычно является показателем «хорошо», и это довольно просто, но все, что я слышал о потоках, говорит о том, что они могут вас укусить неожиданными способами. Мой вопрос: Есть ли лучший способ запустить простой цикл задержки, не блокируя остальную часть приложения?

Ответы [ 5 ]

5 голосов
/ 15 октября 2011

вместо запуска потока для каждого бана, поместите запреты в приоритетную очередь и сделайте так, чтобы один поток выполнял спящий режим и отмену запрета

этот код поддерживает две структуры heapq, что позволяет ему быстро найти самый быстрыйзапрет на истечение срока действия и указание, позволяющее быстро проверить, забанен ли пользователь по имени

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")
3 голосов
/ 15 октября 2011

Зачем вообще нить?

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
2 голосов
/ 15 октября 2011

Использование потокового объекта таймера , например:

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

Тогда в потоке запускается только unban.

0 голосов
/ 15 октября 2011

Если вы используете GUI,
большинство модулей GUI имеют функцию таймера, которая может абстрагировать всю многопоточность и выполнять код по истечении заданного времени, хотя и позволяет выполнять остальную часть кода.

Например, Tkinter имеет функцию after.

0 голосов
/ 15 октября 2011

Это не зависит от языка, но рассмотрим поток, чтобы отслеживать вещи. Поток хранит структуру данных, в которой есть что-то вроде «username» и «banned_until» в таблице. Поток всегда работает в фоновом режиме, проверяя таблицу, и если banned_until истек, он разблокирует пользователя. Другие темы идут нормально.

...