GIL: 多线程反而更慢
Python有个GIL ( Global Interpreter Lock ), 使得同一个 Python 解释器下的多线程任务只能串行执行.
据 UnderstandingGIL 介绍, 有GIL的情况下, 将一个大任务分拆成小任务并调度到多个线程执行, 效率反而大大降低。 这就是GIL的直接后果, 后面Python3.2版本优化GIL之后, 也只能接近单线程的性能。想想 Java 平台的 fork-and-join 等框架, 在Python世界里压根不可能。
在 time.sleep()、IO 操作等会自动释放 GIL, 给其他线程机会切换运行, 有时候造成并行执行的假象。 对于 cpu 计算型任务, 通过 sys.setcheckinterval() 设置上下文切换的频率。
Web 应用: 考虑引入 gevent
Flask 1.0 版本, Flask.run() 默认采用多线程方式运行, 每个并发接入的用户都会 fork 一个线程来响应, 结合上文的分析, Python并不是真正的多线程, 但 epoll IO模型, 对单线程友好(如 Node.js)。因此, 对Flask程序进行改造:
from gevent.wsgi import WSGIServer
from yourapplication import app
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
在我的机器上, 在 100 线程、每个线程循环6次的情况下, 平均响应330ms左右, 多线程 和 gevent 的实现差不多, 但 线程数加到 200 时, gevent的版本还稳定在 330ms, 但 多线程已经到 600ms了, 线程调度的负担越来越重, 请求失败的情况也出现了。
借助 gevent, 不采用生产的WSGI服务器实现, Flask应用性能也能得到不错的提升。 实际上, gunicorn、tornado 等均采用了epoll IO模型。
http://docs.jinkan.org/docs/flask/deploying/wsgi-standalone.html
网友评论