Python如何外部终止线程或在multiprocessing中使用input处理socket并发任务
解决方案
问题根因
- 多进程模式下全局变量是独立拷贝的,父进程修改
login状态不会同步到子进程,子进程的循环无法主动触发退出 - 调用
terminate()强制终止子进程时,子进程正阻塞在input()调用上,标准输入流被强制关闭就会抛出EOFError - 多进程会复制socket对象,父子进程同时持有套接字引用,会导致关闭逻辑异常
最优实现方案:单进程select多路复用
无需多进程/多线程,用select同时监听服务端socket和标准输入,两种事件互不阻塞,要终止时直接退出主循环即可,完全不存在强制终止任务的问题。
修改后的可运行代码如下:
import sys import select import socket from socket import AF_INET, SOCK_STREAM def main(): server_port = int(sys.argv[1]) serverName = 'localhost' clientSocket = socket(AF_INET, SOCK_STREAM) clientSocket.connect((serverName, server_port)) login = False while True: # 同时监听socket和标准输入,超时设为0.1秒避免永久阻塞 read_list, _, _ = select.select([clientSocket, sys.stdin], [], [], 0.1) for fd in read_list: # 服务端有消息下发 if fd == clientSocket: received_message_encrypted = clientSocket.recv(1024) if not received_message_encrypted: # 服务端断开连接 clientSocket.close() return received_message = received_message_encrypted.decode('utf-8') if received_message == 'bye': clientSocket.close() return # 其他服务端消息处理逻辑写在这里 print(received_message) # 登陆成功后才开启输入监听,可根据服务端登陆返回消息修改login状态 if 'login success' in received_message: login = True # 用户有输入 elif fd == sys.stdin and login: command = input() if command == 'logout': clientSocket.send(command.encode('utf-8')) print('user has logged out') clientSocket.close() return elif command == 'whoelse': clientSocket.send(command.encode('utf-8')) else: clientSocket.send('giberish'.encode('utf-8')) if __name__ == "__main__": main()
备选方案(坚持用线程的场景)
如果一定要用多任务拆分输入和接收逻辑,使用daemon线程即可,主进程退出时会自动销毁daemon线程,不会抛出EOF错误:
import sys import threading import socket from socket import AF_INET, SOCK_STREAM login = True clientSocket = None def input_command(): global login while login: command = input() if command == 'logout': clientSocket.send(command.encode('utf-8')) print('user has logged out') login = False break elif command == 'whoelse': clientSocket.send(command.encode('utf-8')) else: clientSocket.send('giberish'.encode('utf-8')) clientSocket.close() def main(): global clientSocket, login server_port = int(sys.argv[1]) serverName = 'localhost' clientSocket = socket(AF_INET, SOCK_STREAM) clientSocket.connect((serverName, server_port)) login = False once = False input_thread = None while True: received_message_encrypted = clientSocket.recv(1024) received_message = received_message_encrypted.decode('utf-8') if received_message == 'bye': login = False clientSocket.close() # 主进程直接退出,daemon线程会被自动回收 sys.exit() if login and not once: once = True # 设置为daemon线程,主进程退出时自动销毁 input_thread = threading.Thread(target=input_command, daemon=True) input_thread.start() if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者Dizzy
相关产品推荐
相关产品推荐

