简体   繁体   中英

Python : how to stop a thread that's waiting for a .recv()

I have this thread running :

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

I wish I could end this thread when I close the Tkinter Window like this :

self.window.protocol('WM_DELETE_WINDOW', self.closeThreads)

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

Can't use thread._close() because it is deprecated and python 3.4 does not allow it.

The only really satisfactory solution I've seen for this problem is not to allow your thread to block inside recv(). Instead, set the socket to non-blocking and have the thread block inside select() instead. The advantage of blocking inside select() is that you can tell select() to return when any one of several sockets becomes ready-for-read, which brings us to the next part: as part of setting up your thread, create a second socket (either a locally-connected TCP socket eg as provided by socketpair , or a UDP socket listening on a port for packets from localhost). When your main thread wants your networking thread to go away, your main thread should send a byte to that socket (or in the TCP case, the main thread could just close its end of the socket-pair). That will cause select() to return ready-for-read on that socket, and when your network thread realizes that the socket is marked ready-for-read, it should respond by exiting immediately.

The advantages of doing it that way are that it works well on all OS's, always reacts immediately (unlike a polling/timeout solution), takes up zero extra CPU cycles when the network is idle, and doesn't have any nasty side effects in multithreaded environments. The downside is that it uses up a couple of extra sockets, but that's usually not a big deal.

Two solutions:

1) Don't stop the thread, just allow it to die when the process exits with sys.exit()

2) Start the thread with a "die now" flag. The Event class is specifically designed to signal one thread from another.

The following example starts a thread, which connects to a server. Any data is handled, and if the parent signals the thread to exit, it will. As an additional safety feature we have an alarm signal to kill everything, just it case something gets out of hand.

source

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()

output

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM