【问题标题】:Python GTK/threading/sockets errorPython GTK/线程/套接字错误
【发布时间】:2010-09-11 23:23:46
【问题描述】:

我正在尝试使用 pyGTK、踏板和套接字构建 Python 应用程序。我遇到了这个奇怪的错误,但考虑到所有涉及的模块,我不完全确定错误在哪里。我对一些打印语句进行了一些调试以缩小范围,我认为错误出现在这段代码的 sn-p 中:

    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.sock.connect(("localhost", 5005))
    self.collectingThread = threading.Thread(target=self.callCollect)
    self.collectingThread.daemon = True
    self.collectingThread.start()

def callCollect(self):
    gobject.timeout_add(500, self.collectData)

def collectData(self):
    print "hello"
    try:
        print self.sock.recv(1024)
    except:
        print "except"
    print "return"
    return True

所以基本上我要做的是设置一个套接字,连接到一个“服务器”脚本(这实际上只是另一个在本地运行的 python 脚本),然后创建一个单独的线程来收集来自服务器脚本的所有传入数据.该线程设置为每 500 毫秒运行一次 collectData 方法。

将打印语句插入到 collectData 方法后,我在运行程序时注意到:

-最初的 GUI 功能齐全

-然后在终端打印以下内容:

hello
**all data received from server script and printed here**
return
hello

-在终端中打印文本后,GUI 变得完全不起作用(按钮无法按下等),我必须强制退出以关闭应用程序

似乎正在发生的事情是线程打印“hello”,打印来自服务器的数据,然后打印“return”。 500 毫秒后,它再次运行 collectData 方法,打印“hello”,然后尝试从服务器打印数据。然而,由于没有数据留下,它引发了一个异常,但由于某种未知原因,它没有执行异常块中的代码,一切都从那里挂起。

知道出了什么问题吗?

【问题讨论】:

    标签: python multithreading sockets gtk


    【解决方案1】:

    timeout_add 正在调度动作发生在主线程上 -- 所以recv 只是阻塞了主线程(当它只是在等待数据时),因此也阻塞了 GUI,所以,除非您设置超时或将套接字设置为非阻塞,否则也不例外。

    您需要将接收委托给线程从计划的操作而不是反之亦然以获得您所追求的效果:让线程例如等待一个事件对象,并且计划的操作每 500 毫秒发出该事件的信号。

    【讨论】:

    • 这是“timeout_add”,而不是“timeout_call”。
    • 我将如何明确地告诉辅助线程等待事件对象及其相应的信号?我检查了 pyGTK 教程中的不同事件,但在套接字上找不到任何东西。抱歉,我对 pyGTK 很陌生。
    • @pythonBOI,我在考虑 Python 的 threading 模块,而不是 GTK -- 参见 docs.python.org/library/threading.html#event-objects -- 在创建子线程之前使用 x = threading.Event() 创建一个全局事件对象 x = threading.Event(),有子线程通过调用x.wait()阻塞等待x,让主线程通过x.set()唤醒子线程(然后通过调用x.clear()将事件对象恢复到静止状态)。没有必要的带套接字或 GTK 的连接,只是通用 Python 线程的东西!
    【解决方案2】:

    不,显然sock.recv 调用会阻塞,因为套接字尚未关闭,并且套接字接收默认情况下是阻塞的。确保在某个时候关闭连接。

    在新线程中运行接收调用会更有意义,否则它可能会阻塞 GUI,因为您当前的实现在 GUI 线程中运行 recv 调用(使用 timeout_add)。只有在非常快地收到答案和/或您必须访问小部件时,您当前的操作方式才有意义。

    顺便说一句,创建一个新线程来调用gobject.timeout_add 是完全没有必要的。 timeout_add()idle_add()注册指定的回调函数并立即返回。 GTK 事件循环然后在超时后自动执行回调(或 idle_add 处于空闲状态)。

    【讨论】:

    • 将套接字设置为非阻塞并将所有内容放在同一个线程中就足够了吗?我可以让套接字保持打开状态(以便它可以继续接收数据)并在用户尝试关闭窗口时关闭它吗?
    • @pythonBOI:这更像是一种 hack,但如果你没有很多这样的套接字,它就可以工作。您不能打开无限数量的套接字,因此您应该在某个时候关闭套接字。我不知道您使用的是哪种通信协议,因此无法为您提供帮助。
    猜你喜欢
    • 1970-01-01
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 2013-05-03
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多