Python GTK/线程/套接字错误
我正在尝试用pyGTK、线程和套接字来构建一个Python应用程序。遇到了一个奇怪的错误,但由于涉及的模块比较多,我不太确定错误出在哪里。我加了一些打印语句来调试,想缩小问题范围,我觉得错误可能出现在这段代码中:
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
- 在终端打印完这些文本后,图形界面就完全失去了响应(按钮无法点击等),我不得不强制退出才能关闭应用程序
看起来发生的情况是,线程打印“hello”,然后打印服务器的数据,最后打印“return”。500毫秒后,它再次运行collectData方法,打印“hello”,然后尝试打印服务器的数据。然而,由于没有剩余的数据,它抛出了一个异常,但由于某种未知原因,它没有执行异常处理块中的代码,结果程序就卡住了。
你觉得问题出在哪里呢?
2 个回答
不,显然,sock.recv
这个调用会阻塞,因为套接字还没有关闭,而套接字接收默认是阻塞的。确保你在某个时候关闭连接。
把接收的调用放在一个新线程里会更合理,否则可能会阻塞图形界面,因为你现在的实现是在图形界面的线程中运行 recv
调用(使用 timeout_add
)。你现在的做法只有在快速收到答案和/或需要访问界面组件时才有意义。
顺便说一下,为了调用 gobject.timeout_add
创建一个新线程是完全不必要的。timeout_add()
和 idle_add()
会注册指定的回调函数并立即返回。GTK事件循环会在超时后自动执行这个回调(或者在空闲状态下对于 idle_add
)。
timeout_add
是在主线程上安排某个动作的执行,所以当 recv
在等待数据时,它会阻塞主线程,这样就会导致图形界面(GUI)也卡住。因此,除非你设置了超时或者把套接字设置为非阻塞,否则不会出现异常。
你需要把接收数据的任务交给一个线程,而不是反过来,这样才能达到你想要的效果:让这个线程等待一个事件对象,然后每500毫秒由安排的动作来触发这个事件。