如何退出多线程程序? [英] How to exit a multithreaded program?

查看:86
本文介绍了如何退出多线程程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was just messing around with threading in python, wrote this basic IM thingy [code at bottom]

我注意到,当我用C-c杀死程序时,它不会退出,只会永远挂起.

I noticed that when I kill the program with C-c it doesn't exit, it just hangs forever.

我只是猜测它正在等待每个线程完成他们正在做的事情,但是由于这是一个永无止境的循环,因此永远不会发生.
所以我想我需要手动杀死每个线程,或者当killsignal进入时结束循环.
我该怎么办?

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.
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在主线程中导致"KeyboardInterrupt"异常.所以你应该在那里处理.
  2. 您的套接字处于阻止模式.这将导致多个套接字函数阻塞调用线程,直到该函数返回为止.在此状态下,线程无法对任何终止事件做出反应.
  3. 正如您已经说过的:线程的run()函数中的无穷循环是...确实无穷无尽.因此,线程执行永无休止(至少在没有意外异常的情况下).您应该使用某种同步对象,例如threading.Event对象,以便能够从外部告诉线程它应该终止自身.
  4. 我不鼓励在主线程之外使用raw_input().想象一下,当您有多个喊线程时会发生什么.
  5. 为什么在Shout类中传输了消息后,为什么总是关闭并重新连接套接字?由于设置成本,仅在特殊情况下才应重新建立网络连接.
  6. 没有用于通信的帧协议,当recv()函数返回时,您永远都无法期望接收到另一主机发送的所有数据.
  7. 线程对象的start()函数不会返回值或对象.因此,保存返回值(= None)并没有多大意义.
  8. 您永远都不能期望send()函数传输所有传递的数据.因此,您必须检查函数的结果并适当地处理并非所有字节都真正传输过的情况.
  9. 要学习线程技术,肯定比网络通信要解决更好的问题,因为该主题本身确实很复杂.

除了所有这些以外,这是我尝试的解决方案.仍有很多可以改进的地方.您也应该考虑Mark Tolonen的答案,因为肯定提供了SocketServer类,以减轻处理此类事件的几件事.但是您也应该继续学习基础知识.

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

这篇关于如何退出多线程程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆