Python:如何停止等待 .recv() 的线程

5

我有一个正在运行的线程:

def run(self):
    while 1:
        msg = self.connection.recv(1024).decode()

我希望能够在关闭Tkinter窗口时结束该线程,例如:
self.window.protocol('WM_DELETE_WINDOW', self.closeThreads)

def closeThreads(self):
    self.game.destroy()
    #End the thread

由于 thread._close() 已被弃用,Python 3.4 不再允许使用该方法。


2
尝试查看您的“connection”对象是否具有非阻塞的“recv”方法。这样,您可以定期轮询数据并检查在“closeThreads”方法中设置的退出标志。如果标志已设置,请退出循环并从“run”函数返回。如果您的“connection”对象是一个“socket”,则可以调用self.connection.setblocking(0)使其变为非阻塞状态。 - jbaiter
2个回答

5
我看到的唯一令人满意的解决方案是不允许您的线程在recv()内部阻塞。相反,将套接字设置为非阻塞,并使线程在select()内部阻塞。在select()内部阻塞的优点在于,您可以告诉select()在多个套接字中的任何一个套接字准备好读取时返回,这带我们来到下一个部分:作为设置线程的一部分,创建第二个套接字(例如由socketpair提供的本地连接的TCP套接字,或侦听来自localhost的端口的UDP套接字)。当主线程想要结束网络线程时,主线程应向该套接字发送一个字节(或者在TCP情况下,主线程可以关闭其套接字对的端口)。这将导致select()在该套接字上返回ready-for-read,当您的网络线程意识到该套接字已准备就绪时,它应立即响应并退出。
以这种方式处理的优点是,在所有操作系统上都能良好运行,总是立即做出反应(不像轮询/超时方案),在网络空闲时不占用任何额外的CPU周期,并且在多线程环境中没有任何不良副作用。缺点是它使用了一些额外的套接字,但这通常不是什么大问题。

在Python中,IO是阻塞的。没有例外。尝试在套接字上调用read(),然后稍后终止该线程。因此,“不要阻塞”不是一个选项。 - Edward Ned Harvey
1
我已经多年使用Python的非阻塞套接字,它运行良好。 - Jeremy Friesner

1

两种解决方案:

1)不要停止线程,只需在进程使用sys.exit()退出时允许其自然结束。

2)使用“立即退出”标志启动线程。 Event类专门设计用于从另一个线程发送信号。

以下示例启动一个线程,连接到服务器。任何数据都将被处理,如果父进程发出退出信号,线程将退出。作为额外的安全功能,我们还有一个警报信号来杀死一切,以防万一情况失控。

来源

import signal, socket, threading

class MyThread(threading.Thread):
    def __init__(self, conn, event):
        super(MyThread,self).__init__()
        self.conn = conn
        self.event = event

    def handle_data(self):
        "process data if any"
        try:
            data = self.conn.recv(4096)
            if data:
                print 'data:',data,len(data)
        except socket.timeout:
            print '(timeout)'

    def run(self):
        self.conn.settimeout(1.0)
        # exit on signal from caller
        while not self.event.is_set():
            # handle any data; continue loop after 1 second
            self.handle_data()
        print 'got event; returning to caller'


sock = socket.create_connection( ('example.com', 80) )
event = threading.Event()

# connect to server and start connection handler
th = MyThread(conn=sock, event=event)

# watchdog: kill everything in 3 seconds
signal.alarm(3)

# after 2 seconds, tell data thread to exit 
threading.Timer(2.0, event.set).start()

# start data thread and wait for it
th.start()
th.join()

输出

(timeout)
(timeout)
got event; returning to caller

6
当线程在等待.recv()时,这并不会使线程停止。 - Janaka Bandara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接