【问题标题】:Python threads exit with ctrl-c in PythonPython 线程在 Python 中使用 ctrl-c 退出
【发布时间】:2017-07-03 23:58:16
【问题描述】:

我有如下 Python 多线程程序。如果我在 5 秒(大约)内按 ctrl+c,它将进入 KeyboardInterrupt 异常

运行代码超过 15 秒无法响应 ctrl+c。 如果我在 15 秒后按 ctrl+c,它不起作用。它不会抛出 KeyboardInterrupt 异常。可能是什么原因 ?我在 Linux 上对此进行了测试。

#!/usr/bin/python

import os, sys, threading, time

class Worker(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    # A flag to notify the thread that it should finish up and exit
    self.kill_received = False

  def run(self):
      while not self.kill_received:
          self.do_something()

  def do_something(self):
      [i*i for i in range(10000)]
      time.sleep(1)

def main(args):

    threads = []
    for i in range(10):
        t = Worker()
        threads.append(t)
        t.start()

    while len(threads) > 0:
        try:
            # Join all threads using a timeout so it doesn't block
            # Filter out threads which have been joined or are None
            threads = [t.join(1) for t in threads if t is not None and t.isAlive()]
        except KeyboardInterrupt:
            print "Ctrl-c received! Sending kill to threads..."
            for t in threads:
                t.kill_received = True

if __name__ == '__main__':
  main(sys.argv)

【问题讨论】:

    标签: python multithreading python-2.7 keyboardinterrupt


    【解决方案1】:

    第一次执行后

    threads = [t.join(1) for t in threads if t is not None and t.isAlive()]
    

    您的变量 threads 包含

    [None, None, None, None, None, None, None, None, None, None]
    

    第二次执行后,同样的变量threads包含:

    []
    

    此时,len(threads) > 0 为 False,您将退出 while 循环。您的脚本仍在运行,因为您有 10 个线程仍然处于活动状态,但是由于您不再在 try / except 块中(以捕获 KeyboardInterrupt),因此您无法停止使用 Ctrl + C

    在您的脚本中添加一些打印以查看我所描述的内容:

    #!/usr/bin/python
    
    import os, sys, threading, time
    
    class Worker(threading.Thread):
      def __init__(self):
        threading.Thread.__init__(self)
        # A flag to notify the thread that it should finish up and exit
        self.kill_received = False
    
      def run(self):
          while not self.kill_received:
              self.do_something()
    
      def do_something(self):
          [i*i for i in range(10000)]
          time.sleep(1)
    
    def main(args):
    
        threads = []
        for i in range(10):
            t = Worker()
            threads.append(t)
            t.start()
            print('thread {} started'.format(i))
    
        while len(threads) > 0:
            print('Before joining')
            try:
                # Join all threads using a timeout so it doesn't block
                # Filter out threads which have been joined or are None
                threads = [t.join(1) for t in threads if t is not None and t.isAlive()]
                print('After join() on threads: threads={}'.format(threads))
    
            except KeyboardInterrupt:
                print("Ctrl-c received! Sending kill to threads...")
                for t in threads:
                    t.kill_received = True
        print('main() execution is now finished...')
    
    if __name__ == '__main__':
      main(sys.argv)
    

    结果:

    $ python thread_test.py
    thread 0 started
    thread 1 started
    thread 2 started
    thread 3 started
    thread 4 started
    thread 5 started
    thread 6 started
    thread 7 started
    thread 8 started
    thread 9 started
    Before joining
    After join() on threads: threads=[None, None, None, None, None, None, None, None, None, None]
    Before joining
    After join() on threads: threads=[]
    main() execution is now finished...
    

    其实 Ctrl + C 不会在 15 秒后停止工作,而是在 10 或 11 秒后停止工作。这是创建和启动 10 个线程(不到一秒)以及在每个线程上执行 join(1) 所需的时间(大约 10 秒)。

    来自the doc的提示:

    由于join()总是返回None,你必须在join()之后调用isAlive()来判断是否超时——如果线程还活着,join()调用就超时了。

    【讨论】:

      【解决方案2】:

      为了跟进上面的海报,isAlive() 被重命名为 is_alive() 在 Python 3.9.6 上尝试过

      完整代码:

      #!/usr/bin/python
      
      import os, sys, threading, time
      
      class Worker(threading.Thread):
        def __init__(self):
          threading.Thread.__init__(self)
          # A flag to notify the thread that it should finish up and exit
          self.kill_received = False
      
        def run(self):
            while not self.kill_received:
                self.do_something()
      
        def do_something(self):
            [i*i for i in range(10000)]
            time.sleep(1)
      
      def main(args):
      
          threads = []
          for i in range(10):
              t = Worker()
              threads.append(t)
              t.start()
              print('thread {} started'.format(i))
      
          while len(threads) > 0:
              print('Before joining')
              try:
                  # Join all threads using a timeout so it doesn't block
                  # Filter out threads which have been joined or are None
                  threads = [t.join(1) for t in threads if t is not None and t.is_alive()]
                  print('After join() on threads: threads={}'.format(threads))
      
              except KeyboardInterrupt:
                  print("Ctrl-c received! Sending kill to threads...")
                  for t in threads:
                      t.kill_received = True
          print('main() execution is now finished...')
      
      if __name__ == '__main__':
        main(sys.argv)
      

      【讨论】:

      猜你喜欢
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-10
      • 1970-01-01
      • 1970-01-01
      • 2017-06-21
      • 2014-03-17
      相关资源
      最近更新 更多