【问题标题】:Python - Can't kill main thread with KeyboardInterruptPython - 无法使用 KeyboardInterrupt 杀死主线程
【发布时间】:2013-08-01 10:07:29
【问题描述】:

我正在制作一个简单的多线程端口扫描器。它扫描主机上的所有端口并返回打开的端口。问题是中断扫描。完成扫描需要很长时间,有时我希望在扫描过程中使用 C-c 杀死程序。问题是扫描不会停止。主线程被锁定在 queue.join() 并且忽略了 KeyboardInterrupt,直到队列中的所有数据都被处理,从而解除阻塞主线程并优雅地退出程序。我所有的线程都被守护了,所以当主线程死时,他们应该和他一起死。

我尝试使用信号库,但没有成功。重写 threading.Thread 类并添加优雅终止的方法不起作用......主线程在执行 queue.join() 时不会收到 KeyboardInterrupt

import threading, sys, Queue, socket

queue = Queue.Queue()

def scan(host):
    while True:
        port = queue.get()

        if port > 999 and port % 1000 == 0:
            print port
        try:
            #sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            #sock.settimeout(2) #you need timeout or else it will try to connect forever! 
            #sock.connect((host, port))
            #----OR----
            sock = socket.create_connection((host, port), timeout = 2)

            sock.send('aaa')
            data = sock.recv(100)
            print "Port {} open, message: {}".format(port, data)
            sock.shutdown()
            sock.close()
            queue.task_done()
        except:
            queue.task_done()


def main(host):
    #populate queue
    for i in range(1, 65536):
        queue.put(i)
    #spawn worker threads
    for port in range(100):
        t = threading.Thread(target = scan, args = (host,))
        t.daemon = True
        t.start()

if __name__ == '__main__':
    host = ""

    #does input exist?
    try:
        host = sys.argv[1]
    except:
        print "No argument was recivied!"
        exit(1)

    #is input sane?
    try:
        host = socket.gethostbyname(host)
    except:
        print "Adress does not exist"
        exit(2)

    #execute main program and wait for scan to complete
    main(host)
    print "Post main() call!"
    try:
        queue.join()
    except KeyboardInterrupt:
        print "C-C"
        exit(3)

编辑:

我找到了使用时间模块的解决方案。

#execute main program and wait for scan to complete
main(host)

#a little trick. queue.join() makes main thread immune to keyboardinterrupt. So use queue.empty() with time.sleep()
#queue.empty() is "unreliable" so it may return True a bit earlier then intented.
#when queue is true, queue.join() is executed, to confirm that all data was processed.
#not a true solution, you can't interrupt main thread near the end of scan (when queue.empty() returns True)
try:
    while True:
        if queue.empty() == False:
            time.sleep(1)
        else:
            break
except KeyboardInterrupt:
    print "Alas poor port scanner..."
    exit(1)
queue.join()

【问题讨论】:

  • 有谁知道是否有办法在短时间内做到这一点 真的吗?这浪费了大量的 CPU,我正在使用线程中的线程来避免一段时间为真。 thread.join 太完美了,只要我能杀死它(没有浪费 CPU 时间的无限循环)。有任何想法吗?我将要运行的测试不需要我杀死它,因为线程无限期地继续(理论上它会永远继续),但是,为了测试目的和将来的参考(因为在另一个术语中做pkill python 很痛苦。窗口每次),我真的很想知道。谢谢(不要说 ctrl+z)
  • 如果你在 Windows 上运行它,这是“正常的”。 Python 解释器捕获 CTRL-C 并设置一个内部标志,然后等待控制返回到 Python 解释器,以便它可以引发KeyboardInterrupt。它依靠阻塞系统调用来返回EINTR,以便返回并检查该内部标志。但是,在发生这种情况时,Windows 上的阻塞系统操作不会返回 EINTR 错误代码,因此 KeyboardInterrupt 异常会延迟到阻塞操作完成。

标签: python multithreading keyboardinterrupt


【解决方案1】:

您已经创建了线程守护进程,但是您需要在守护线程存在时保持主线程处于活动状态,这是如何做到的:Cannot kill Python script with Ctrl-C

【讨论】:

  • 我的主线程还活着。 queue.join() 阻塞线程,直到队列中的所有数据都被处理完,然后 queue.join() 解除对主线程的阻塞,程序正常终止。问题是当主线程到达 queue.join() 时,它使线程“免疫”到 KeyboardInterrupt。如果我不能杀死主线程,我就不能杀死守护线程。我会澄清我的问题...
  • 愿意分享解决方案吗?
【解决方案2】:

创建线程时,将它们添加到正在运行的线程列表中,并在处理 ctrl-C 时向列表中的每个线程发送终止信号。这样您就可以主动清理而不是依赖它为您完成。

【讨论】:

    猜你喜欢
    • 2019-05-01
    • 2015-04-22
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 2021-11-13
    • 1970-01-01
    • 2017-06-18
    相关资源
    最近更新 更多