【发布时间】:2016-10-09 06:40:05
【问题描述】:
我创建了一个函数,它使用 PyQt5 “渲染” HTML 并返回结果。如下:
def render(source_html):
"""Fully render HTML, JavaScript and all."""
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebKitWidgets import QWebPage
class Render(QWebPage):
def __init__(self, html):
self.html = None
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().setHtml(html)
self.app.exec_()
def _loadFinished(self, result):
self.html = self.mainFrame().toHtml()
self.app.quit()
return Render(source_html).html
有时它的线程会无限期挂起,我将不得不终止整个程序。不幸的是,PyQt5 也可能是一个黑匣子,因为我不知道当它行为不端时如何杀死它。
理想情况下,我可以实现 n 秒的超时。作为一种解决方法,我已将该函数放在它自己的脚本 render.py 中,并通过 subprocess 以这种怪物方式调用它:
def render(html):
"""Return fully rendered HTML, JavaScript and all."""
args = ['render.py', '-']
timeout = 20
try:
return subprocess.check_output(args,
input=html,
timeout=timeout,
universal_newlines=True)
# Python 2's subprocess.check_output doesn't support input or timeout
except TypeError:
class SubprocessError(Exception):
"""Base exception from subprocess module."""
pass
class TimeoutExpired(SubprocessError):
"""
This exception is raised when the timeout expires while
waiting for a child process.
"""
def __init__(self, cmd, timeout, output=None):
super(TimeoutExpired, self).__init__()
self.cmd = cmd
self.timeout = timeout
self.output = output
def __str__(self):
return ('Command %r timed out after %s seconds' %
(self.cmd, self.timeout))
process = subprocess.Popen(['timeout', str(timeout)] + args,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
# pipe html into render.py's stdin
output = process.communicate(
html.encode('utf8'))[0].decode('latin1')
retcode = process.poll()
if retcode == 124:
raise TimeoutExpired(args, timeout)
return output
multiprocessing 模块似乎大大简化了事情:
from multiprocessing import Pool
pool = Pool(1)
rendered_html = pool.apply_async(render, args=(html,)).get(timeout=20)
pool.terminate()
有没有办法实现不需要这些恶作剧的超时?
【问题讨论】:
-
强行杀死一个线程(在任何语言中)几乎都不是一个好主意,因为不可能保证线程没有持有锁,正在修改共享状态等。也就是说,你看过非 Qt 特定的 approaches to killing threads in Python 吗?
-
AFAIK 我无权访问 PyQt5 的线程(或者如果我有,我不确定如何访问它们)。它一直导致我的程序无限期地停顿,尽管它可能是不可取的,但我只需要一种方法在它无法恢复时将其杀死。看起来
multiprocessing可能能够完成这项工作,这将比当前的 hack 略有改进。 -
也许你可以找出它挂起的原因,然后避免这种情况。
-
@Trilarion 当然,这是我的第一笔生意。不幸的是,它甚至很难重现,我只在每周左右挂起它的每日预定过程后才注意到。我怀疑这是由于某些网页上与广告相关的 JS。我注意到许多其他人报告了我怀疑的类似问题:qtforum.org/article/32057/…qtcentre.org/archive/index.php/t-28766.html
-
QT 对我来说很大程度上是个谜,但我认为你可以用
render(requests.get('https://news.ycombinator.com').text)重现类似的东西。这对我来说是无限期的。
标签: python multithreading pyqt pyqt5