如何退出多线程程序?

时间:2011-05-01 15:55:32

标签: python multithreading

我只是在python中搞乱线程,写了这个基本的IM东西[底部的代码]

我注意到当我用C-c杀死程序时它没有退出,它只是永远挂起。

我只是猜测它正在等待每个线程完成他们正在做的事情,但因为它是一个永远不会发生的无限循环。
所以我想我需要手动杀死每个线程,或者在killsignal进来时结束循环。
我该怎么做?

#!/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()

2 个答案:

答案 0 :(得分:7)

我在代码中看到了不良行为的几个原因。

  1. Ctrl + C在主线程中导致“KeyboardInterrupt”异常。所以你应该在那里处理它。
  2. 您的套接字处于阻止模式。这会导致几个套接字函数阻塞调用线程,直到函数返回。在此状态期间,线程无法对任何终止事件做出反应。
  3. 正如你已经说过的:你在线程的run()函数中的无限循环是......真的无穷无尽。所以线程执行永远不会结束(至少没有意外的异常)。您应该使用某种同步对象,如threading.Event对象,以便能够从外部告诉线程它应该自行终止。
  4. 我不鼓励在主线程中使用raw_input()。想象一下当你有多个Shout线程时会发生什么。
  5. 当您在Shout课程中传输消息时,为什么总是关闭并重新连接套接字?由于设置成本,应仅在特殊情况下重新建立网络连接。
  6. 如果没有用于通信的帧协议,当recv()函数返回时,您永远不会期望接收到由其他主机发送的所有数据。
  7. 线程对象的start()函数不返回值或对象。因此保存返回值(= None)没有多大意义。
  8. 您永远不会指望send()函数传输所有传递的数据。因此,必须检查函数的结果,并在没有真正传输所有字节时适当地处理这种情况。
  9. 要学习线程,解决问题肯定比网络通信更好,因为这个话题本身就很复杂。
  10. 除了所有这些,这是我尝试解决方案。还有很多可以改进的地方。您也应该考虑Mark Tolonen的答案,因为SocketServer类肯定会用来处理这类东西时的几个方面。但你也应该继续研究基础知识。

    #!/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()
    

答案 1 :(得分:5)

查看SocketServer.py的Python库源代码,特别是server_forever()的实现,以了解服务器如何实现退出。它使用select()轮询服务器套接字以获取新连接并测试退出标志。这是你使用SocketServer的源代码的黑客攻击,我向Shout()添加了一个退出标志。它将运行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()