Python中用Ctrl+C终止多线程程序的问题解决

时间:2021-10-22 03:22:18 


#!/bin/env python
 # -*- coding: utf-8 -*-
 #filename: peartest.py

 import threading, signal

 is_exit = False

 def doStress(i, cc):
     global is_exit
     idx = i
     while not is_exit:
         if (idx < 10000000):
             print "thread[%d]: idx=%d"%(i, idx)
             idx = idx + cc
         else:
             break
     print "thread[%d] complete."%i

 def handler(signum, frame):
     global is_exit
     is_exit = True
     print "receive a signal %d, is_exit = %d"%(signum, is_exit)

 if __name__ == "__main__":
     signal.signal(signal.SIGINT, handler)
     signal.signal(signal.SIGTERM, handler)
     cc = 5
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i,cc))
         t.start()

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

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


threads=[]
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i, cc))
         t.setDaemon(True)
         threads.append(t)
         t.start()
     for i in range(cc):
         threads[i].join()

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


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

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


#!/bin/env python
 # -*- coding: utf-8 -*-
 #filename: peartest.py

 import threading, signal

 is_exit = False

 def doStress(i, cc):
     global is_exit
     idx = i
     while not is_exit:
         if (idx < 10000000):
             print "thread[%d]: idx=%d"%(i, idx)
             idx = idx + cc
         else:
             break
     if is_exit:
         print "receive a signal to exit, thread[%d] stop."%i
     else:
         print "thread[%d] complete."%i

 def handler(signum, frame):
     global is_exit
     is_exit = True
     print "receive a signal %d, is_exit = %d"%(signum, is_exit)

 if __name__ == "__main__":
     signal.signal(signal.SIGINT, handler)
     signal.signal(signal.SIGTERM, handler)
     cc = 5
     threads = []
     for i in range(cc):
         t = threading.Thread(target=doStress, args=(i,cc))
         t.setDaemon(True)
         threads.append(t)
         t.start()
     while 1:
         alive = False
         for i in range(cc):
             alive = alive or threads[i].isAlive()
         if not alive:
             break

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

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

标签:Ctrl+C,终止多线程
0
投稿

猜你喜欢

  • 详解Laravel服务容器的优势

    2023-10-31 03:36:04
  • 利用JAVA反射,读取数据库表名,自动生成对应实体类的操作

    2024-01-14 23:58:29
  • Python pandas DataFrame基础运算及空值填充详解

    2022-01-01 22:19:49
  • 教你如何用python开发一款数字推盘小游戏

    2021-11-03 23:22:14
  • Mac下安装mysql5.7 完整步骤(图文详解)

    2024-01-24 16:11:36
  • python数组循环处理方法

    2023-08-03 16:36:19
  • Python如何读写CSV文件

    2023-03-23 08:41:13
  • python比较两个列表是否相等的方法

    2023-04-10 06:29:18
  • 如何使用Vue3设计实现一个Model组件浅析

    2024-04-27 15:57:19
  • Python使用lambda表达式对字典排序操作示例

    2022-12-26 06:27:46
  • element弹窗表格的字体模糊bug解决

    2024-04-18 10:53:25
  • 几种修复ACCESS数据库的实用方法

    2008-11-20 17:37:00
  • Mysql常见的慢查询优化方式总结

    2024-01-26 02:17:04
  • JS对img标签进行优化使用onerror显示默认图像

    2024-04-10 10:51:35
  • python os.listdir按文件存取时间顺序列出目录的实例

    2021-03-21 23:02:52
  • pandas多级分组实现排序的方法

    2022-05-06 14:16:11
  • Python面向对象之多态原理与用法案例分析

    2023-08-16 00:09:59
  • ASP.NET 2.0防止同一用户同时登录

    2007-10-03 14:30:00
  • IDEA 2020 设置项目集成git 及svn和git之间的切换问题

    2022-11-23 20:02:14
  • python读取当前目录下的CSV文件数据

    2022-04-06 16:50:35
  • asp之家 网络编程 m.aspxhome.com