【发布时间】:2020-03-08 15:41:53
【问题描述】:
我正在编写一个 Python (v3.7.3) 套接字服务器,我想为其使用阻塞 I/O。我使用select() 没有超时来接受新客户以及从中读取。我可以关闭监听套接字以中止 select(),并捕获 OSError 作为停止执行的指示。
但是,当在单独的线程中运行时,这似乎不起作用,我不明白为什么。
我知道还有其他方法可以完成此操作,例如使用超时、为 select() 使用虚拟套接字或与侦听器建立虚拟连接以将其唤醒。但是这些都在某种程度上违背了使用 select() 的目的,并且在单线程中运行时没有必要。
这是一个重现问题的基本示例,在我的实际代码中,它仅代表多个线程中的一个(因此,我首先使用线程):
#!/usr/bin/env python3
import signal
import socket
import threading
class SocketCloseTest:
"""Simple test case for using socket.close() to abort select.select()"""
def __init__(self, port, address=None):
self.port = port
self.address = address or ''
self.socket = None
def stop(self):
"""Close listening socket to stop select.select()"""
if self.socket:
print("Closing listener", self.socket)
self.socket.close()
print("Listener closed", self.socket)
def threaded_run(self):
"""Run test in a separate thread"""
thread = threading.Thread(target=self.run)
print("Starting sub-thread")
thread.start()
thread.join()
print("Sub-thread ended")
def run(self):
"""Run test"""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Reuse port for quick re-launch of the application
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.address, self.port))
print("Starting listener")
self.socket.listen()
try:
print("select() started")
r, w, e = select.select([self.socket], [], [])
except OSError:
print("select() aborted")
else:
print("select() completed")
if __name__ == '__main__':
tester = SocketCloseTest(5000, address='')
# Set up signal handler for Ctrl-C
def signal_handler(signum, frame):
print("Received signal {}".format(signum))
tester.stop()
signal.signal(signal.SIGINT, signal_handler)
# This works
tester.run()
# This doesn't work
# tester.threaded_run()
print("Main thread ended")
当使用test.run() 时,它会按预期运行,结果如下:
开始监听
选择开始
^C收到信号2
关闭监听器
监听器关闭
选择取消
主线程结束
但是,当使用tester.threaded_run() 运行时,它只会挂起对 select() 的调用应该中止的位置。奇怪的是,此时将作业置于后台会导致代码继续运行:
启动子线程
启动监听器
选择开始
^C收到信号2
关闭监听器
监听器关闭
--在这里按下Ctrl-Z 可以在shell 中暂停作业--$ bg
--Shell 在后台报告作业--
选择取消
子线程结束
主线程结束
谢谢……
- 编辑提到
accept()患有完全相同的症状。
【问题讨论】:
标签: python multithreading sockets select