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信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。

 

转载于:https://www.cnblogs.com/wowarsenal/archive/2013/03/29/python_multithread.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值