【问题标题】:How to avoid too many threads when using tornado.ioloop.IOLoop.run_in_executor?使用tornado.ioloop.IOLoop.run_in_executor时如何避免线程过多?
【发布时间】:2019-08-29 09:25:15
【问题描述】:

我正在使用tornado.ioloop.IOLoop.run_in_executor 将同步函数更改为异步,但事实证明,每次调用该函数时,都会创建一个线程但没有杀死。

这是一个最小可重现的例子(至少在我的机器上可以重现):

#!/usr/bin/env python3                                                          

import time
import tornado.ioloop
import tornado.web

def slow_func():
    time.sleep(1)
    print('slow func returned')
    return 'succ\n'

class TestHandler(tornado.web.RequestHandler):
    async def get(self):
        print('GET called')
        try:
            result = await tornado.ioloop.IOLoop.current().run_in_executor(None, slow_func)
        except Exception as e:
            print(e)
        self.write(result)
        print('GET returned')

if __name__ == '__main__':
    tornado.web.Application([
        (r'/', TestHandler),
    ]).listen(3000)
    print('Serving at 3000')
    tornado.ioloop.IOLoop.current().start()

对该TestHandler 的每个请求都会创建一个新线程来运行slow_func,但该线程在函数返回后仍然存在。我可以在ps H 中看到它们,它会创建新线程,直到达到 ulimit。我这里的环境是:

$ uname -a
Linux xxx 2.6.32-754.6.3.el6.x86_64 #1 SMP Tue Sep 18 10:29:08 EDT 2018 x86_64 x86_64 x86_64 GNU/Linux
$ python3 --version
Python 3.7.4
$ pip3 show tornado
Name: tornado
Version: 6.0.3
Summary: Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.
Home-page: http://www.tornadoweb.org/
Author: Facebook
Author-email: python-tornado@googlegroups.com
License: http://www.apache.org/licenses/LICENSE-2.0
Location: /xxx/lib/python3.7/site-packages
Requires: 
Required-by:

tornado.ioloop.IOLoop.run_in_executor 使用 concurrent.futures.Executor 并返回一个可等待的 Future 对象。 [1][2][3]

函数返回后线程在做什么?为什么在未来对象解决后他们不被杀死?我应该如何避免这种情况?

【问题讨论】:

  • 我已经弄清楚为什么其他人没有遇到这个问题:concurrent.futures.ThreadPoolExecutor的默认大小是CPU核心的5倍。这里我有 384 个内核,但 ulimit -u 只有 1024 个。
  • 仅供参考,这在 Python 3.8 中被更改为默认使用最多 32 个线程。

标签: python-3.x multithreading tornado python-asyncio concurrent.futures


【解决方案1】:

run_in_executorconcurrent.futures.Executor 对象作为第一个参数。

你可以创建一个执行器并限制线程池的大小:

from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=8)

IOLoop.current().run_in_executor(executor, slow_func) 

【讨论】:

  • 您的解决方案有效。当线程池已满时,请求将排队。非常感谢你。除了重用线程之外,这种设计的目的是什么?为什么线程不自动退出?
  • @simonmysun 在 Python 中,线程在其 run() 方法退出时退出。但是在这种情况下,如果您不提供自己的执行程序,run_in_executor 默认使用ThreadPoolExecutor。因此线程不会退出并保持活动状态,因为默认情况下ThreadPoolExecutor 正在管理它们。
  • 是的,谢谢您提供的信息。最后我发现它的默认限制是 5 * cpu 核心数,而我的 ulimit -u 只有 1024。所以这就是大多数用户没有达到 ulimit 的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-24
相关资源
最近更新 更多