如果我使用socket.makefile然后关闭文件对象以及底层套接字,那么对read
的后续调用将抛出异常,就像我想要的那样。例如,以下代码按我的预期工作:
import socket
from time import sleep
from threading import Thread
ADDR = ("localhost", 4321)
def listener(sock):
client,addr = sock.accept()
sleep(1)
client.close()
sock.close()
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind(ADDR)
server_sock.listen(1)
Thread(target=listener, args=[server_sock]).start()
sock = socket.create_connection(ADDR)
f = sock.makefile("r+b", bufsize=0)
f.close()
sock.close()
f.read(8) # throws an exception, as I'd expect
但是,如果我在文件/套接字仍处于打开状态时调用read
,那么该调用将被阻止,然后然后如果我关闭套接字,则read方法仍然没有不回来实际上,它会无限期挂起,直到另一端关闭套接字。以下代码演示了这种令人痛苦的行为:
import socket
from time import sleep
from threading import Thread
ADDR = ("localhost", 4321)
def reader(f):
print("about to read")
print("we read %r" % f.read(8))
print("finished reading")
def listener(sock):
client, addr = sock.accept()
sleep(3)
client.close()
sock.close()
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind(ADDR)
server_sock.listen(1)
Thread(target=listener, args=[server_sock]).start()
sock = socket.create_connection(ADDR)
f = sock.makefile("r+b", bufsize=0)
Thread(target=reader, args=[f]).start()
sleep(1)
print("closing pseudo-file and socket")
f.close()
sock.close()
sleep(1)
print("we still haven't finished reading!")
对我来说这是一个严重的问题,因为我想在一个线程中对f.read
进行阻塞调用,但是仍然可以关闭套接字并让线程从该调用返回(可能通过抛出异常)并退出。但是,所有发生的情况是,只要另一方从不关闭套接字,呼叫就会永久阻塞。
那么Thread1
是否有办法在read
创建的类似文件的对象上调用socket.makefile
,然后让Thread2
以一种方式关闭套接字导致Thread1
在read
来电时停止阻止?
编辑:我尝试重新编写程序以完全使用gevent
及其套接字和Greenlet方法进行多线程处理,但我的程序仍然完全相同:
from gevent import sleep, socket, spawn
ADDR = ("localhost", 4321)
def reader(f):
print("about to read")
print("we read %r" % f.read(8))
print("finished reading")
def listener(sock):
client, addr = sock.accept()
sleep(3)
client.close()
sock.close()
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind(ADDR)
server_sock.listen(1)
spawn(listener, server_sock)
sock = socket.create_connection(ADDR)
f = sock.makefile("r+b", bufsize=0)
spawn(reader, f)
sleep(1)
print("closing pseudo-file and socket")
f.close()
sock.close()
sleep(1)
print("we still haven't finished reading!")
sleep(2)
我很惊讶地发现即使使用gevent
套接字,对read
的调用也会在基础套接字关闭后阻止。如果没有办法阻止这种情况,我可能只是接受托马斯的压抑“这是不可能的”答案:(
答案 0 :(得分:4)
在这种情况下,我使用eventlet取得了成功,而现在,gevent正在做同样的事情:
'猴子修补'套接字库以使用非阻塞I / O.以下是您可以尝试的示例:
>>> from gevent import monkey; monkey.patch_socket()
>>> import socket
并了解这会对结果产生什么影响
答案 1 :(得分:1)
是的,线程有令人痛苦的行为,特别是当同时从多个线程接触套接字之类的东西时。这不是你可以正常工作的东西。没有办法强制一个在读取中被阻塞的线程突破读取或终止 - 从它下面关闭套接字更可能导致你的整个程序出现段错误(或者更糟)想。
处理这种情况的正确方法是使用非阻塞读取。一个体面的事件框架,如Twisted,可以帮助你解决这个问题。