• python多线程ctrl-c退出问题


    场景:

    经常会遇到下述问题:很多io busy的应用采取多线程的方式来解决,但这时候会发现python命令行不响应ctrl-c 了,而对应的java代码则没有问题:

    Java代码  收藏代码
    1. public class Test {  
    2.     public static void main(String[] args) throws Exception {  
    3.   
    4.         new Thread(new Runnable() {  
    5.   
    6.             public void run() {  
    7.                 long start = System.currentTimeMillis();  
    8.                 while (true) {  
    9.                     try {  
    10.                         Thread.sleep(1000);  
    11.                     } catch (Exception e) {  
    12.                     }  
    13.                     System.out.println(System.currentTimeMillis());  
    14.                     if (System.currentTimeMillis() - start > 1000 * 100) break;  
    15.                 }  
    16.             }  
    17.         }).start();  
    18.   
    19.     }  
    20. }  

    java Test

    ctrl-c则会结束程序

    而对应的python代码:

    Python代码  收藏代码
    1. # -*- coding: utf-8 -*-  
    2. import time  
    3. import threading  
    4. start=time.time()  
    5. def foreverLoop():  
    6.     start=time.time()  
    7.     while 1:  
    8.         time.sleep(1)  
    9.         print time.time()  
    10.         if time.time()-start>100:  
    11.             break  
    12.               
    13.   
    14. thread_=threading.Thread(target=foreverLoop)  
    15. #thread_.setDaemon(True)  
    16. thread_.start()  

    python p.py

    后ctrl-c则完全不起作用了。

    不成熟的分析:

    首先单单设置 daemon 为 true 肯定不行,就不解释了。当daemon为 false 时,导入python线程库后实际上,threading会在主线程执行完毕后,检查是否有不是 daemon 的线程,有的化就wait,等待线程结束了,在主线程等待期间,所有发送到主线程的信号也会被阻测,可以在上述代码加入signal模块验证一下:

    Python代码  收藏代码
    1. def sigint_handler(signum,frame):    
    2.     print "main-thread exit"  
    3.     sys.exit()    
    4. signal.signal(signal.SIGINT,sigint_handler)  

    在100秒内按下ctrl-c没有反应,只有当子线程结束后才会出现打印 "main-thread exit",可见 ctrl-c被阻测了

    threading 中在主线程结束时进行的操作:

    Python代码  收藏代码
    1. _shutdown = _MainThread()._exitfunc  
    2. def _exitfunc(self):  
    3.         self._Thread__stop()  
    4.         t = _pickSomeNonDaemonThread()  
    5.         if t:  
    6.             if __debug__:  
    7.                 self._note("%s: waiting for other threads", self)  
    8.         while t:  
    9.             t.join()  
    10.             t = _pickSomeNonDaemonThread()  
    11.         if __debug__:  
    12.             self._note("%s: exiting", self)  
    13.         self._Thread__delete()  

     对所有的非daemon线程进行join等待,其中join中可自行察看源码,又调用了wait,同上文分析 ,主线程等待到了一把锁上。

    不成熟的解决:

    只能把线程设成daemon才能让主线程不等待,能够接受ctrl-c信号,但是又不能让子线程立即结束,那么只能采用传统的轮询方法了,采用sleep间歇省点cpu吧:

    Python代码  收藏代码
    1. # -*- coding: utf-8 -*-  
    2. import time,signal,traceback  
    3. import sys  
    4. import threading  
    5. start=time.time()  
    6. def foreverLoop():  
    7.     start=time.time()  
    8.     while 1:  
    9.         time.sleep(1)  
    10.         print time.time()  
    11.         if time.time()-start>5:  
    12.             break  
    13.               
    14. thread_=threading.Thread(target=foreverLoop)  
    15. thread_.setDaemon(True)  
    16. thread_.start()  
    17.   
    18. #主线程wait住了,不能接受信号了  
    19. #thread_.join()  
    20.   
    21. def _exitCheckfunc():  
    22.     print "ok"  
    23.     try:  
    24.         while 1:  
    25.             alive=False  
    26.             if thread_.isAlive():  
    27.                 alive=True  
    28.             if not alive:  
    29.                 break  
    30.             time.sleep(1)    
    31.     #为了使得统计时间能够运行,要捕捉  KeyboardInterrupt :ctrl-c        
    32.     except KeyboardInterrupt, e:  
    33.         traceback.print_exc()  
    34.     print "consume time :",time.time()-start  
    35.           
    36. threading._shutdown=_exitCheckfunc  

       缺点:轮询总会浪费点cpu资源,以及battery.

    有更好的解决方案敬请提出。

    ps1: 进程监控解决方案 :

    用另外一个进程来接受信号后杀掉执行任务进程,牛

    Python代码  收藏代码
    1. # -*- coding: utf-8 -*-  
    2. import time,signal,traceback,os  
    3. import sys  
    4. import threading  
    5. start=time.time()  
    6. def foreverLoop():  
    7.     start=time.time()  
    8.     while 1:  
    9.         time.sleep(1)  
    10.         print time.time()  
    11.         if time.time()-start>5:  
    12.             break  
    13.   
    14. class Watcher:  
    15.     """this class solves two problems with multithreaded 
    16.     programs in Python, (1) a signal might be delivered 
    17.     to any thread (which is just a malfeature) and (2) if 
    18.     the thread that gets the signal is waiting, the signal 
    19.     is ignored (which is a bug). 
    20.  
    21.     The watcher is a concurrent process (not thread) that 
    22.     waits for a signal and the process that contains the 
    23.     threads.  See Appendix A of The Little Book of Semaphores. 
    24.     http://greenteapress.com/semaphores/ 
    25.  
    26.     I have only tested this on Linux.  I would expect it to 
    27.     work on the Macintosh and not work on Windows. 
    28.     """  
    29.   
    30.     def __init__(self):  
    31.         """ Creates a child thread, which returns.  The parent 
    32.             thread waits for a KeyboardInterrupt and then kills 
    33.             the child thread. 
    34.         """  
    35.         self.child = os.fork()  
    36.         if self.child == 0:  
    37.             return  
    38.         else:  
    39.             self.watch()  
    40.   
    41.     def watch(self):  
    42.         try:  
    43.             os.wait()  
    44.         except KeyboardInterrupt:  
    45.             # I put the capital B in KeyBoardInterrupt so I can  
    46.             # tell when the Watcher gets the SIGINT  
    47.             print 'KeyBoardInterrupt'  
    48.             self.kill()  
    49.         sys.exit()  
    50.   
    51.     def kill(self):  
    52.         try:  
    53.             os.kill(self.child, signal.SIGKILL)  
    54.         except OSError: pass  
    55.   
    56. Watcher()              
    57. thread_=threading.Thread(target=foreverLoop)  
    58. thread_.start()  

     注意 watch()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

  • 相关阅读:
    telnet连接ip
    完成端口————留着看
    mysql设置最大连接数量
    前端页面唯一字符串生成(Js)UUID
    java加密MD5实现及密码验证
    cookie中存取中文字符
    自定义标签——带标签体
    简单讨论数据类型(byte)强制转化后的数值变化规律
    般若波罗蜜多心经(转载)
    table 表头固定
  • 原文地址:https://www.cnblogs.com/rrxc/p/4044961.html
Copyright © 2020-2023  润新知