• Python中用Ctrl+C终止多线程程序


    摘要:

    python多线程中要响应Ctrl+C的信号以杀死整个进程,需要:

    1. 把所有子线程设为Daemon;
    2. 使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成;
    3. 写一个响应Ctrl+C信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。

    花了一天时间用python为服务写了个压力测试。很简单,多线程向服务器发请求。但写完之后发现如果中途想停下来,按Ctrl+C达不到效果,自然想到要用信号处理函数捕捉信号,使线程都停下来。模拟代码如下:

     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 #filename: peartest.py
     4 
     5 import threading, signal
     6 
     7 is_exit = False
     8 
     9 def doStress(i, cc):
    10     global is_exit
    11     idx = i
    12     while not is_exit:
    13         if (idx < 10000000):
    14             print "thread[%d]: idx=%d"%(i, idx)
    15             idx = idx + cc
    16         else:
    17             break
    18     print "thread[%d] complete."%i
    19 
    20 def handler(signum, frame):
    21     global is_exit
    22     is_exit = True
    23     print "receive a signal %d, is_exit = %d"%(signum, is_exit)
    24 
    25 if __name__ == "__main__":
    26     signal.signal(signal.SIGINT, handler)
    27     signal.signal(signal.SIGTERM, handler)
    28     cc = 5
    29     for i in range(cc):
    30         t = threading.Thread(target=doStress, args=(i,cc))
    31         t.start()

    上面是一个模拟程序,并不真正向服务发送请求,而代之以在一千万以内,每个线程每隔并发数个(cc个)打印一个整数。很明显,当所有线程都完成自己的任务后,进程会正常退出。但如果我们中途想退出(试想一个压力测试程序,在中途已经发现了问题,需要停止测试),该肿么办?你当然可以用ps查找到进程号,然后kill -9杀掉,但这样太繁琐了,捕捉Ctrl+C是最自然的想法。上面示例程序中已经捕捉了这个信号,并修改全局变量is_exit,线程中会检测这个变量,及时退出。

    但事实上这个程序并不work,当你按下Ctrl+C时,程序照常运行,并无任何响应。网上搜了一些资料,明白是python的子线程如果不是daemon的话,主线程是不能响应任何中断的。但设为daemon后主线程会随之退出,接着整个进程很快就退出了,所以还需要在主线程中检测各个子线程的状态,直到所有子线程退出后自己才退出,因此上例29行之后的代码可以修改为:

    1     threads=[]
    2     for i in range(cc):
    3         t = threading.Thread(target=doStress, args=(i, cc))
    4         t.setDaemon(True)
    5         threads.append(t)
    6         t.start()
    7     for i in range(cc):
    8         threads[i].join()

    重新试一下,问题依然没有解决,进程还是没有响应Ctrl+C,这是因为join()函数同样会waiting在一个锁上,使主线程无法捕获信号。因此继续修改,调用线程的isAlive()函数判断线程是否完成:

    1     while 1:
    2         alive = False
    3         for i in range(cc):
    4             alive = alive or threads[i].isAlive()
    5         if not alive:
    6             break

    这样修改后,程序完全按照预想运行了:可以顺利的打印每个线程应该打印的所有数字,也可以中途用Ctrl+C终结整个进程。完整的代码如下:

     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 #filename: peartest.py
     4 
     5 import threading, signal
     6 
     7 is_exit = False
     8 
     9 def doStress(i, cc):
    10     global is_exit
    11     idx = i
    12     while not is_exit:
    13         if (idx < 10000000):
    14             print "thread[%d]: idx=%d"%(i, idx)
    15             idx = idx + cc
    16         else:
    17             break
    18     if is_exit:
    19         print "receive a signal to exit, thread[%d] stop."%i
    20     else:
    21         print "thread[%d] complete."%i
    22 
    23 def handler(signum, frame):
    24     global is_exit
    25     is_exit = True
    26     print "receive a signal %d, is_exit = %d"%(signum, is_exit)
    27 
    28 if __name__ == "__main__":
    29     signal.signal(signal.SIGINT, handler)
    30     signal.signal(signal.SIGTERM, handler)
    31     cc = 5
    32     threads = []
    33     for i in range(cc):
    34         t = threading.Thread(target=doStress, args=(i,cc))
    35         t.setDaemon(True)
    36         threads.append(t)
    37         t.start()
    38     while 1:
    39         alive = False
    40         for i in range(cc):
    41             alive = alive or threads[i].isAlive()
    42         if not alive:
    43             break

    其实,如果用python写一个服务,也需要这样,因为负责服务的那个线程是永远在那里接收请求的,不会退出,而如果你想用Ctrl+C杀死整个服务,跟上面的压力测试程序是一个道理。总结一下,python多线程中要响应Ctrl+C的信号以杀死整个进程,需要:

    1. 把所有子线程设为Daemon;
    2. 使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成;
    3. 写一个响应Ctrl+C信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。
  • 相关阅读:
    1.8 Hello World添加menu
    1.7 HelloWorld 添加视图
    1.6 Hello World
    1.5 组件开发基础
    awk
    sed
    grep / egrep
    Shell基础知识
    和管道符有关的命令
    Shell变量
  • 原文地址:https://www.cnblogs.com/wowarsenal/p/python_multithread.html
Copyright © 2020-2023  润新知