使用`threading.Thread`的烧瓶后台任务阻塞了主线程

时间:2019-08-15 19:30:54

标签: python multithreading asynchronous flask eventlet

我有一个长期运行的后台任务,它将再次旋转Flask应用程序并在后台进行一些审核。前端是一个Web应用程序,并使用socketio与后端主烧瓶应用程序进行通信,以处理多种异步行为。

我确保仅在创建主线程时才触发后台任务,并且仅在脚本的开头执行eventlet.monkey_patch()

如果后台线程有很多要审核的内容,则它将阻塞主线程,内存中的内容越多,则阻塞主线程的时间就越长。审计根本不占用CPU,只是一些数据库插入和日志记录。

  • 需要审核的项目从主线程添加到内存中的对象中,并通过引用传递给子线程。 (就像内存中的队列一样)
  • 如果我不安装猴子补丁事件,那么一切正常,但是flask的自动重装将无法正常工作,因此我需要开发它。
  • 我在dev中运行类似于socketio.run(app)的应用程序
  • 使用gunicorn / eventlet时行为仍然存在
  • 后台任务处于休眠状态sleep(2)时,没有任何阻塞。

import eventlet
eventlet.monkey_patch()

# ... rest of code is a basic flask app create_app factory that at some # point starts the new thread if it's the main thread

# the async code that runs is the following

class AsyncAuditor(threading.Thread):

    def __init__(self, tasks: list, stop: threading.Event):
        super().__init__()
        self.tasks = tasks
        self.stop_event = stop

    def run(self):
        from app import init_app
        from dal import db

        app = init_app(mode='sys')
        app.logger.info('starting async audit thread')

        with app.app_context():
            try:
                while not self.stop_event.is_set():
                    if len(self.tasks) > 0:
                        task: dict
                        for task in self.tasks:
                            app.logger.debug(threading.current_thread().name + ' new audit record')
                            task.payload = encryptor.encrypt(task.payload)
                            task.ip = struct.unpack("!I", socket.inet_aton(task.ip))[0]
                            db.session.add(task)
                        self.tasks.clear()
                        db.session.commit()
                    sleep(2)
                app.logger.info('exiting async audit thread')
            except BaseException as e:
                app.logger.exception('Exception')

# there's some code that tries to gracefully exit if app needs to exit

stop_event = threading.Event()
async_task = AsyncAuditor(API.audit_tasks, stop_event)
async_task.start()

def exit_async_thread():
    stop_event.set()
    async_task.join()


atexit.register(exit_async_thread)


我希望在子线程工作时,主线程不会被任何db操作阻塞,实际上,就像我之前提到的那样,如果我没有猴子补丁事件子集,那么在主线程中一切正常还有一个孩子取而代之的是,当后台任务正在运行时,在烧瓶应用程序中命中端点时,我得到了9甚至30秒的延迟。

0 个答案:

没有答案