如何使用线程模块调用函数?

时间:2017-10-30 20:55:11

标签: python mysql multithreading

或者换句话说,如何创建延时功能?
我有一个python机器人应该在使用某些命令时向用户的关注者发送通知。

例如,如果Tim运行命令' >跟随汤姆,所有蒂姆的粉丝都将在下午通知他跟随汤姆,汤姆会被告知蒂姆跟着他。

我已经使用大量的关注者测试了这个功能,并且机器人保持稳定并且避免被踢出服务器,我猜测因为for循环会为发送给每个关注者的每条消息增加延迟。 / p>

我遇到的问题是,如果两个用户同时运行一个保证发出通知的命令。机器人立即离线。所以我需要的是在运行通知功能之前添加一个人工延迟。 Time.sleep(),不起作用。它只会冻结整个程序,并将每个命令保存在队列中。 (如果两个用户运行>跟随,它将睡眠2秒,并在延迟后运行他们的命令)

我尝试使用线程模块来替换time.sleep()。我的通知功能如下。

#message is the message to be sent, var is the username to use
def notify(message,var):
      #SQL connect 
      dbconfig = read_db_config()
      conn = MySQLConnection(**dbconfig)
      cursor = conn.cursor()
      #choose all of user's followers
      cursor.execute('select username from users where notifications=0 and username IN (select follower from followers where followed like "{}")'.format(var))
      results = cursor.fetchall()
      #for each , send a PM
      for result in results:
        self.pm.message(ch.User(str(result[0])), message)
      conn.close()  

那么我如何使用线程来做到这一点?我尝试了几种方法,但让我们选择最糟糕的方式。

def example(_):
    username = 'bob'
    # _ is equal to args
    a = notify("{} is now following {}.".format(username,_),username)
    c =threading.Timer(2,a)
    c.start()

这将在响应中抛出Nonetype错误。

  

线程中的异常Thread-1:Traceback(最近一次调用最后一次):
  文件" /usr/lib/python2.7/threading.py",第810行,在__bootstrap_inner中       self.run()File" /usr/lib/python2.7/threading.py" ;,第1082行,在运行中       self.function(* self.args,** self.kwargs)TypeError:' NoneType'对象不可调用

注意:我认为这种方法会起作用,会有很多用户同时使用机器人,所以直到它中断这似乎是一个修复。

2 个答案:

答案 0 :(得分:3)

以下是一些可能有用的代码。请注意notify处理结果的方式的更改。

import threading
import Queue

def notifier(nq):
    # Read from queue until None is put on queue.
    while True:
        t = nq.get()
        try:
            if t is None:
                break
            func, args = t
            func(*args)
            time.sleep(2) # wait 2 seconds before sending another notice
        finally:
            nq.task_done()


# message is the message to be sent, var is the username to use, nq is the
# queue to put notification on.
def notify(message, var, nq):
    #SQL connect
    dbconfig = read_db_config()
    conn = MySQLConnection(**dbconfig)
    cursor = conn.cursor()
    #choose all of user's followers
    cursor.execute('select username from users where notifications=0 and username IN (select follower from followers where followed like "{}")'.format(var))
    results = cursor.fetchall()
    #for each , send a PM
    for result in results:
        # Put the function to call and its args on the queue.
        args = (ch.User(str(result[0])), message)
        nq.put((self.pm.message, args))
    conn.close()


if __name__ == '__main__':
    # Start a thread to read from the queue.
    nq = Queue.Queue()
    th = threading.Thread(target=notifier, args=(nq,))
    th.daemon = True
    th.start()
    # Run bot code
    # ...
    #
    nq.put(None)
    nq.join() # block until all tasks are done

答案 1 :(得分:1)

我会尝试使用类似我在下面写的类的线程锁。

这将导致只有一个线程能够在任何给定时间发送PM。

class NotifyUsers():
    def __init__(self, *args, **kwargs):
        self.notify_lock = threading.Lock()
        self.dbconfig = read_db_config()
        self.conn = MySQLConnection(**dbconfig)
        self.cursor = self.conn.cursor()

    def notify_lock_wrapper(self, message, var):
        self.notify_lock.acquire()
        try:
            self._notify(message, var)
        except:
            # Error handling here
            pass
        finally:
            self.notify_lock.release()

    def _notify(self, message, var):
        #choose all of user's followers
        self.cursor.execute('select username from users where notifications=0 and username IN (select follower from followers where followed like "{}")'.format(var))
        results = self.cursor.fetchall()

        #for each, send a PM
        for result in results:
            self.pm.message(ch.User(str(result[0])), message)