扭曲+ qtreactor:最后一个窗口关闭后如何清理?

发布于 2024-10-27 13:02:40 字数 1089 浏览 1 评论 0原文

我有一个 Twisted/PyQt 应用程序(除其他外)连接到一堆远程资源。当用户关闭窗口时,我想关闭所有连接,如果可能的话干净地关闭,否则强制关闭。

问题是,当我去关闭连接时,反应堆似乎不再允许我这样做。

这是我的应用程序代码:

# Create app and connect the Twisted/Qt reactors
app = QApplication(sys.argv)
qtreactor.qt4reactor.install()

# Shutdown Twisted when window is closed
@defer.inlineCallbacks
def stop():
    print "="*40, "Closing connections..."
    yield closeConnections()
    print "="*40, "closed."

    print "="*40, "Stopping reactor..."
    reactor.stop()
    print "="*40, "stopped."
app.connect(app, SIGNAL("lastWindowClosed()"), stop)

reactor.runReturn()
rc = app.exec_()
exit(rc)

这是我的清理代码的精简版本:

@defer.inlineCallbacks
def closeConnections():
    for connection in connections:
        print "Closing connection #%s" % connection
        yield threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
        print "Connection closed."

到达第一个打印语句,并且执行命令,但我从未得到第二个,也没有多次执行 for 循环。

我的分析正确吗?问题是反应堆已经关闭了,所以我再也没有收到threads.deferToThread 的回复吗?或者还有其他问题吗?此外,我该如何解决它?

谢谢, 乔纳森

I have a Twisted/PyQt application that (among other things) connects to a bunch of remote resources. When the user closes the window I want to shut down all the connections, cleanly if possible, forcibly if not.

The problem is that by the time I go to close the connections, it appears that the reactor is no longer alive to let me do so.

Here's my app code:

# Create app and connect the Twisted/Qt reactors
app = QApplication(sys.argv)
qtreactor.qt4reactor.install()

# Shutdown Twisted when window is closed
@defer.inlineCallbacks
def stop():
    print "="*40, "Closing connections..."
    yield closeConnections()
    print "="*40, "closed."

    print "="*40, "Stopping reactor..."
    reactor.stop()
    print "="*40, "stopped."
app.connect(app, SIGNAL("lastWindowClosed()"), stop)

reactor.runReturn()
rc = app.exec_()
exit(rc)

And here's a stripped down version of my cleanup code:

@defer.inlineCallbacks
def closeConnections():
    for connection in connections:
        print "Closing connection #%s" % connection
        yield threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))
        print "Connection closed."

The first print statement is reached, and the command is executed, but I never get the second one, nor do I go through the for loop multiple times.

Is my analysis correct? Is the problem that the reactor is already down, so I never hear back from threads.deferToThread? Or is there some other problem? Furthermore, how do I fix it?

Thanks,
Jonathan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

东走西顾 2024-11-03 13:02:40

我不知道 lastWindowClosed() 信号何时触发。然而,即使它足够早地触发,在反应堆关闭之前(阻止你做你想做的事情),我确信 PyQt 不知道如何处理你的 停止功能。这意味着当您的异步清理代码尝试运行时,关闭过程将顺利进行。 GUI 关闭可能会在网络关闭之前完成。

因此,请改用reactor.addSystemEventTrigger('before', 'shutdown', stop)。我不知道这是否会比 lastWindowClosed() 稍早或稍晚运行,但它会运行得足够早,以便反应堆仍然可用,并且<将会注意您的函数返回的 Deferred。事实上,关闭将被暂停,直到 Deferred 触发。这为您提供了进行清理所需的所有时间。

除此之外,您不应该执行 threads.deferToThread(popen("/foo/bar/cleanup %s" % connection))

  • 您需要将可调用对象传递给 deferToThread< /code>,不是调用可调用对象的结果。正如所写,您的代码在反应器线程中运行 popen 并将文件对象传递给要调用的线程(当然,这没有意义)
  • 混合线程和子进程是不确定的。我不知道,大多数时候你可能会侥幸逃脱。
  • reactor.spawnProcess 可以让你在没有阻塞、没有线程的情况下运行子进程,并且不用担心线程和进程的混合。如果您不需要 spawnProcess 的所有功能(您似乎不需要),另请参阅 twisted.internet.utils.getProcessOutput

I don't know exactly when that lastWindowClosed() signal fires. However, even if it fires early enough, before the reactor has shut down (preventing you from doing what you want to do), I'm sure that PyQt doesn't know what to do with the Deferred that is returned by your stop function. This means that the shutdown process will proceed merrily onward while your asynchronous cleanup code tries to run. Likely the GUI shutdown will finish before your network shutdown gets anywhere.

So, use reactor.addSystemEventTrigger('before', 'shutdown', stop) instead. I don't know if this will run slightly earlier or slightly later than lastWindowClosed(), but it will run early enough that the reactor will still be usable, and it will pay attention to the Deferred your function returns. Shutdown will be suspended, in fact, until that Deferred fires. This gives you all the time you need to do your cleanup.

Separately from all that, you shouldn't do threads.deferToThread(popen("/foo/bar/cleanup %s" % connection)):

  • You need to pass a callable to deferToThread, not the result of calling the callable. As written, your code runs popen in the reactor thread and passes a file object to the thread to be called (which makes no sense, of course)
  • Mixing threads and child processes is iffy. You might get away with it most of the time, I dunno.
  • reactor.spawnProcess will let you run a child process without blocking, without threads, and without worrying about mixing threads and processes. See also twisted.internet.utils.getProcessOutput if you don't need all the features of spawnProcess (which you appear not to).
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文