python结束多线程_Python中用Ctrl+C终止多线程程序

摘要:

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

把所有子线程设为Daemon;

使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成;

写一个响应Ctrl+C信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。

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

1 #!/bin/env python

2 #-*- coding: utf-8 -*-

3 #filename: peartest.py

4

5 importthreading, signal6

7 is_exit =False8

9 defdoStress(i, cc):10 globalis_exit11 idx =i12 while notis_exit:13 if (idx < 10000000):14 print "thread[%d]: idx=%d"%(i, idx)15 idx = idx +cc16 else:17 break

18 print "thread[%d] complete."%i19

20 defhandler(signum, frame):21 globalis_exit22 is_exit =True23 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 inrange(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 inrange(cc):3 t = threading.Thread(target=doStress, args=(i, cc))4 t.setDaemon(True)5 threads.append(t)6 t.start()7 for i inrange(cc):8 threads[i].join()

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

1 while 1:2 alive =False3 for i inrange(cc):4 alive = alive orthreads[i].isAlive()5 if notalive:6 break

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

1 #!/bin/env python

2 #-*- coding: utf-8 -*-

3 #filename: peartest.py

4

5 importthreading, signal6

7 is_exit =False8

9 defdoStress(i, cc):10 globalis_exit11 idx =i12 while notis_exit:13 if (idx < 10000000):14 print "thread[%d]: idx=%d"%(i, idx)15 idx = idx +cc16 else:17 break

18 ifis_exit:19 print "receive a signal to exit, thread[%d] stop."%i20 else:21 print "thread[%d] complete."%i22

23 defhandler(signum, frame):24 globalis_exit25 is_exit =True26 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 inrange(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 =False40 for i inrange(cc):41 alive = alive orthreads[i].isAlive()42 if notalive:43 break

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

把所有子线程设为Daemon;

使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成;

写一个响应Ctrl+C信号的函数,修改全局变量,使得各子线程能够检测到,并正常退出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值