简体   繁体   English

如何退出多线程程序?

[英]How to exit a multithreaded program?

I was just messing around with threading in python, wrote this basic IM thingy [code at bottom]我只是在搞乱 python 中的线程,写了这个基本的 IM thingy [底部代码]

I noticed that when I kill the program with Cc it doesn't exit, it just hangs forever.我注意到当我用 Cc 杀死程序时它不会退出,它只是永远挂起。

I'm just guessing it's waiting for each thread to finish what they are doing, but since it's an endless loop that will never happen.我只是猜测它正在等待每个线程完成他们正在做的事情,但因为这是一个永远不会发生的无限循环。
So I guess I need to kill each thread manually, or end the loop when the killsignal comes in.所以我想我需要手动杀死每个线程,或者在killsignal进来时结束循环。
How would I do that?我该怎么做?

#!/usr/bin/env python
import threading
import socket

class Listen(threading.Thread):

    def run(self):
        conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        conn.bind(('', 2727))
        conn.listen(1)
        while True:
            channel, details = conn.accept()
            print str(details)+": "+channel.recv(250)
            channel.send("got it")
            channel.close()

class Shout(threading.Thread):

    def run(self):
        while True:
            try:    
                address = raw_input("who u talking to? ")
                conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                conn.connect((address, 2727))
                break
            except:
                print "can't connect to "+ str(address)
        while True:
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            conn.connect((address, 2727))
            conn.send(raw_input())
            conn.close()

listen = Listen().start()
shout = Shout().start()

I see several causes of the misbehavior in your code.我在您的代码中看到了导致不当行为的几个原因。

  1. Ctrl+C causes a "KeyboardInterrupt" exception in the main thread. Ctrl+C 在主线程中导致“KeyboardInterrupt”异常。 So you should handle it there.所以你应该在那里处理它。
  2. Your socket is in blocking mode.您的套接字处于阻塞模式。 This causes several socket functions to block the calling thread until the function returns.这会导致几个套接字函数阻塞调用线程,直到 function 返回。 During this state the thread cannot react to any termination event.在此 state 期间,线程无法对任何终止事件做出反应。
  3. As you already said: your endless loop in the thread's run() function is... really endless.正如你已经说过的:你在线程的 run() function 中的无限循环是......真的是无限的。 So the thread execution is never ending (at least not without an unexpected exception).所以线程执行永远不会结束(至少不会没有意外异常)。 You should use some kind of synchronization object, like an threading.Event object, to be able to tell a thread externally that it should terminate itself.您应该使用某种同步 object,例如 threading.Event object,以便能够在外部告诉线程它应该自行终止。
  4. I would discourage the use of raw_input() out of the main thread.我不鼓励在主线程之外使用 raw_input() 。 Imagine what happens when you have more than one Shout thread.想象一下当你有多个 Shout 线程时会发生什么。
  5. Why are you always closing and reconnecting the socket when a message has been transmitted in your Shout class?为什么在您的 Shout class 中传输消息时,您总是关闭并重新连接套接字? Network connections should be re-established only in special cases because of the setup costs.由于设置成本,应仅在特殊情况下重新建立网络连接。
  6. Without a frame protocol for the communication you can never expect to have received all data that was sent by the other host when the recv() function returns.如果没有用于通信的帧协议,当 recv() function 返回时,您永远不会期望收到其他主机发送的所有数据。
  7. The start() function of the thread object does not return a value or object.线程 object 的 start() function 没有返回值或 object。 So saving the returned value (=None) doesn't make much sense.所以保存返回值 (=None) 没有多大意义。
  8. You can never expect the send() function to transmit all passed data.您永远不能期望 send() function 传输所有传递的数据。 Therefore you must check the result of the function and appropriately handle the situation when not all bytes were really transmitted.因此,您必须检查 function 的结果,并适当处理并非所有字节都真正传输的情况。
  9. To learn threading there are surely better problems to solve than network communication, since that topic is in itself really complex.要学习线程,肯定有比网络通信更好的问题需要解决,因为该主题本身非常复杂。

Beside all these things, here is my try for a solution.除了所有这些之外,这是我尝试的解决方案。 Still there is much that can be improved.仍然有很多可以改进的地方。 You should consider the answer from Mark Tolonen too, since the SocketServer class is surely provided to ease several things in handling this kind of stuff.您也应该考虑 Mark Tolonen 的回答,因为肯定提供了 SocketServer class 来简化处理此类事情的几件事。 But you should keep on studying the basics too.但是你也应该继续学习基础知识。

#!/usr/bin/env python
import threading
import socket
import time
import errno

class StoppableThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop_event = threading.Event()        

    def stop(self):
        if self.isAlive() == True:
            # set event to signal thread to terminate
            self.stop_event.set()
            # block calling thread until thread really has terminated
            self.join()

class Accept(StoppableThread):
    def __init__(self, port):
        StoppableThread.__init__(self)
        self.port = port
        self.threads = []

    def run(self):     
        # handle connection acception
        conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        conn.bind(('', self.port ))
        conn.listen(5)
        # set socket timeout to ~10ms
        conn.settimeout(0.01)
        while self.stop_event.is_set() == False:
            try:
                csock, caddr = conn.accept()
                # spawn a new thread to handle the client connection
                listen_thread = Listen(csock, caddr)
                self.threads.append(listen_thread)
                listen_thread.start()
            except socket.timeout:
                # socket operation timeout
                # clear all terminated threads from thread list                
                for thread in self.threads:
                    if thread.isAlive() == False:
                        self.threads.remove(thread)

        self.stop_threads()

    def stop_threads(self):
        # stop all running threads
        for listen_thread in self.threads:
            if listen_thread.isAlive() == True:
                listen_thread.stop()
        self.threads = [] 

class Listen(StoppableThread):
    def __init__(self, csock, caddr):
        StoppableThread.__init__(self)
        self.csock = csock
        self.caddr = caddr
        self.csock.setblocking(False)

    def run(self):                
        while self.stop_event.is_set() == False:            
            try:                
                recv_data = self.csock.recv(250)
                if len(recv_data) > 0:       
                    print str(self.caddr)+": " + recv_data
                    self.csock.send("got it")                    
                else:
                    # connection was closed by foreign host
                    self.stop_event.set()
            except socket.error as (sock_errno, sock_errstr):
                if (sock_errno == errno.EWOULDBLOCK):
                    # socket would block - sleep sometime
                    time.sleep(0.1)                    
                else:
                    # unexpected / unhandled error - terminate thread
                    self.stop_event.set()
        channel.close()

class Shout(StoppableThread):
    def __init__(self, sport):
        StoppableThread.__init__(self)
        self.sport = sport

    def run(self):
        while self.stop_event.is_set() == False:
            try:    
                address = raw_input("who u talking to? ")
                conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                conn.connect((address, self.sport))
                break
            except socket.error:
                # handle connection problems
                print "can't connect to "+ str(address)
            except: 
                # exit thread in case of an unexpected error
                self.stop_event.set()

        while self.stop_event.is_set() == False:
            try: 
                # chat loop: send messages to remote host            
                print "what to send? :",
                msg = raw_input()
                # beware: send() function may block indefinitly here and it might not send all bytes as expected !!
                conn.send(msg)
            except:
                # exit thread in case of an unexpected error
                self.stop_event.set()
        # close socket before thread terminates
        conn.close()

def main():
    do_exit = False
    server_port = 2727

    # start server socket thread
    accept = Accept(server_port)
    accept.start()

    # start transmitting client socket thread
    shout = Shout(server_port)
    shout.start()

    while do_exit == False:
        try:
            # sleep some time
            time.sleep(0.1)
        except KeyboardInterrupt:
            # Ctrl+C was hit - exit program
            do_exit = True

    # stop all running threads
    shout.stop()
    accept.stop()

    # exit main program after all threads were terminated gracefully    

if __name__ == "__main__":
    main()

Look at the Python library source for SocketServer.py, in particular the implementation of server_forever() to see how a server implements a quit.查看 SocketServer.py 的 Python 库源代码,特别是 server_forever() 的实现,以了解服务器如何实现退出。 It uses select() to poll the server socket for new connections and tests a quit flag.它使用 select() 来轮询服务器套接字以获取新连接并测试退出标志。 Here's a hack on your source to use SocketServer, and I added a quit flag to Shout().这是使用 SocketServer 的源代码的 hack,我在 Shout() 中添加了退出标志。 It will run the Shout and Listen threads for 5 seconds and then stop them.它将运行 Shout 和 Listen 线程 5 秒钟,然后停止它们。

import socket
import SocketServer
import threading
import time

class Handler(SocketServer.StreamRequestHandler):
    def handle(self):
        print str(self.client_address) + ": " + self.request.recv(250)
        self.request.send("got it\n")

class Listen(threading.Thread):
    def run(self):
        self.server = SocketServer.TCPServer(('',2727),Handler)
        self.server.serve_forever()
    def stop(self):
        self.server.shutdown()

class Shout(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.quit = False
    def run(self):
        while not self.quit:
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            conn.connect(('localhost', 2727))
            conn.send('sending\n')
            print conn.recv(100)
            conn.close()
    def stop(self):
        self.quit = True

listen = Listen()
listen.start()
shout = Shout()
shout.start()

time.sleep(5)

shout.stop()
listen.stop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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