在多线程Python中查找占用CPU的插件
我有一个用Python写的系统,它处理大量数据,使用了几个不同开发者写的插件,这些开发者的经验水平各不相同。
简单来说,这个应用程序会启动几个工作线程,然后给它们提供数据。每个线程会决定使用哪个插件来处理某个数据项,并请求插件处理这个数据。插件其实就是一个Python模块,里面定义了特定的功能。处理数据通常会用到正则表达式,处理时间一般不会超过一秒钟。
不过,有时候某个插件会花费几分钟才完成处理,这段时间CPU的使用率会达到100%。这种情况通常是因为某个不太优化的正则表达式和某个特定的数据项组合在一起,导致效率低下。
这时候事情就变得复杂了。如果我怀疑哪个插件有问题,我可以查看它的代码找到问题所在。但有时候我并不那么幸运。
- 我不能只用单线程。如果这样做,可能要花费几周才能重现这个问题。
- 给插件加个计时器也没用,因为当它卡住的时候,整个程序的全局解释器锁(GIL)也会被占用,其他插件也会花几分钟才能完成。
- (如果你在想,SRE引擎不会释放GIL。)
- 据我所知,在多线程环境下进行性能分析几乎没什么用。
除了把整个架构重写成多进程,还有什么办法可以找出是谁在占用我所有的CPU资源吗?
补充说明: 针对一些评论的回复:
在Python中分析多线程代码并不实用,因为性能分析工具测量的是函数的总时间,而不是活跃的CPU时间。你可以试试cProfile.run('time.sleep(3)')来理解我的意思。(感谢rog的最后一条评论)。
使用单线程的原因很棘手,因为只有每20,000个数据项中有1个会导致问题,而我不知道是哪一个。使用多线程可以让我在大约一个小时内处理20,000个数据项,而单线程可能需要更长时间(因为涉及很多网络延迟)。还有一些更复杂的情况,我现在不想深入讨论。
不过,尝试将调用插件的特定代码序列化,以便一个插件的处理时间不会影响其他插件的处理时间,这个主意不错。我会试试这个方法,并反馈结果。
4 个回答
正如你所说,由于GIL的存在,在同一个进程中是无法做到的。
我建议你启动一个第二个监控进程,这个进程会监听你原始应用中另一个线程的“心跳”。一旦这个心跳在规定的时间内消失,监控进程就可以结束你的应用并重新启动它。
我还是建议你看看nosklo的提议。你可以在单线程上进行分析,找出问题所在,然后在你长时间运行的程序中获取数据,可能会发现问题的根源。是的,我知道有2万项内容,这会花费很长时间,但有时候你就是得耐心点,找到那个麻烦的东西,才能让自己相信问题已经被发现并解决了。运行脚本后,可以去做点其他有意义的事情。等回来再分析结果。这就是有时候能把真正的高手和新手区分开的地方;-)
或者,你也可以添加一些日志信息,记录每个插件处理每一项时所花的时间。在程序运行结束时查看日志数据,看看哪个插件的运行时间特别长,相比其他的来说。
看起来你并不需要多线程,只需要并发,因为你的线程之间没有共享任何状态:
试试多进程,而不是多线程
就是用一个线程和多个子进程。这样你可以给每个请求计时,因为没有全局解释器锁(GIL)的限制。
另一种方法是去掉多个执行线程,改用基于事件的网络编程(比如使用twisted)。