场景:
经常会遇到下述问题:很多io busy的应用采取多线程的方式来解决,但这时候会发现python命令行不响应ctrl-c 了,而对应的java代码则没有问题:
public class Test { public static void main(String[] args) throws Exception { new Thread(new Runnable() { public void run() { long start = System.currentTimeMillis(); while (true) { try { Thread.sleep(1000); } catch (Exception e) { } System.out.println(System.currentTimeMillis()); if (System.currentTimeMillis() - start > 1000 * 100) break; } } }).start(); } } java Test
ctrl-c则会结束程序
而对应的python代码:
# -*- coding: utf-8 -*- import time import threading start=time.time() def foreverLoop(): start=time.time() while 1: time.sleep(1) print time.time() if time.time()-start>100: break thread_=threading.Thread(target=foreverLoop) #thread_.setDaemon(True) thread_.start()
python p.py
后ctrl-c则完全不起作用了。
不成熟的分析:
首先单单设置 daemon 为 true 肯定不行,就不解释了。当daemon为 false 时,导入python线程库后实际上,threading会在主线程执行完毕后,检查是否有不是 daemon 的线程,有的化就wait,等待线程结束了,在主线程等待期间,所有发送到主线程的信号也会被阻测,可以在上述代码加入signal模块验证一下:
def sigint_handler(signum,frame): print "main-thread exit" sys.exit() signal.signal(signal.SIGINT,sigint_handler)
在100秒内按下ctrl-c没有反应,只有当子线程结束后才会出现打印 "main-thread exit",可见 ctrl-c被阻测了
threading 中在主线程结束时进行的操作:
_shutdown = _MainThread()._exitfunc def _exitfunc(self): self._Thread__stop() t = _pickSomeNonDaemonThread() if t: if __debug__: self._note("%s: waiting for other threads", self) while t: t.join() t = _pickSomeNonDaemonThread() if __debug__: self._note("%s: exiting", self) self._Thread__delete()
对所有的非daemon线程进行join等待,其中join中可自行察看源码,又调用了wait,同上文分析 ,主线程等待到了一把锁上。
不成熟的解决:
只能把线程设成daemon才能让主线程不等待,能够接受ctrl-c信号,但是又不能让子线程立即结束,那么只能采用传统的轮询方法了,采用sleep间歇省点cpu吧:
# -*- coding: utf-8 -*- import time,signal,traceback import sys import threading start=time.time() def foreverLoop(): start=time.time() while 1: time.sleep(1) print time.time() if time.time()-start>5: break thread_=threading.Thread(target=foreverLoop) thread_.setDaemon(True) thread_.start() #主线程wait住了,不能接受信号了 #thread_.join() def _exitCheckfunc(): print "ok" try: while 1: alive=False if thread_.isAlive(): alive=True if not alive: break time.sleep(1) #为了使得统计时间能够运行,要捕捉 KeyboardInterrupt :ctrl-c except KeyboardInterrupt, e: traceback.print_exc() print "consume time :",time.time()-start threading._shutdown=_exitCheckfunc
缺点:轮询总会浪费点cpu资源,以及battery.
有更好的解决方案敬请提出。
ps1: 进程监控解决方案 :
用另外一个进程来接受信号后杀掉执行任务进程,牛
# -*- coding: utf-8 -*- import time,signal,traceback,os import sys import threading start=time.time() def foreverLoop(): start=time.time() while 1: time.sleep(1) print time.time() if time.time()-start>5: break class Watcher: """this class solves two problems with multithreaded programs in Python, (1) a signal might be delivered to any thread (which is just a malfeature) and (2) if the thread that gets the signal is waiting, the signal is ignored (which is a bug). The watcher is a concurrent process (not thread) that waits for a signal and the process that contains the threads. See Appendix A of The Little Book of Semaphores. http://greenteapress.com/semaphores/ I have only tested this on Linux. I would expect it to work on the Macintosh and not work on Windows. """ def __init__(self): """ Creates a child thread, which returns. The parent thread waits for a KeyboardInterrupt and then kills the child thread. """ self.child = os.fork() if self.child == 0: return else: self.watch() def watch(self): try: os.wait() except KeyboardInterrupt: # I put the capital B in KeyBoardInterrupt so I can # tell when the Watcher gets the SIGINT print 'KeyBoardInterrupt' self.kill() sys.exit() def kill(self): try: os.kill(self.child, signal.SIGKILL) except OSError: pass Watcher() thread_=threading.Thread(target=foreverLoop) thread_.start()
注意 watch()一定要放在线程创建前,原因未知。。。。,否则立刻就结束
本文向大家介绍探寻Android的线程问题,包括了探寻Android的线程问题的使用技巧和注意事项,需要的朋友参考一下 什么是线程? 线程或者线程执行本质上就是一串命令(也是程序代码),然后我们把它发送给操作系统执行。 Multithreaded_process 一般来说,我们的CPU在任何时候一个核只能处理一个线程。多核处理器(目前大多数Android设备已经都是多核)顾名思义,就是可以同时处理
本文向大家介绍解决Python中定时任务线程无法自动退出的问题,包括了解决Python中定时任务线程无法自动退出的问题的使用技巧和注意事项,需要的朋友参考一下 python的线程有一个类叫Timer可以,用来创建定时任务,但是它的问题是只能运行一次,如果要重复执行,则只能在任务中再调用一次timer,但这样就存在新的问题了,就是在主进程退出后,不能正常退出子线程。 象上面这样,就可以每10秒调用一
问题内容: 如何在多进程python程序中捕获+ ,并正常退出所有进程,我需要在Unix和Windows上均可使用的解决方案。我尝试了以下方法: 这是可行的,但我认为这不是正确的解决方案。 问题答案: 正确的方法来处理/用是: 在创建流程之前,请忽略该流程。这样创建的子进程继承了处理程序。 创建a之后,在父进程中还原原始处理程序。 使用和代替阻塞和。 等待结果超时,因为默认阻塞将等待忽略所有信号。
本文向大家介绍Java多线程的临界资源问题解决方案,包括了Java多线程的临界资源问题解决方案的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java多线程的临界资源问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 临界资源问题的原因:某一个线程在对临界资源进行访问时,还没来得及完全修改临界资源的值,临界资源就被其他线程拿去
本文向大家介绍python线程中的同步问题及解决方法,包括了python线程中的同步问题及解决方法的使用技巧和注意事项,需要的朋友参考一下 多线程开发可能遇到的问题 假设两个线程t1和t2都要对num=0进行增1运算,t1和t2都各对num修改1000000次,num的最终的结果应该为2000000。但是由于是多线程访问,有可能出现下面情况: 运行结果可能不一样,但是结果往往不是2000000。问
本文向大家介绍解决Python出现_warn_unsafe_extraction问题的方法,包括了解决Python出现_warn_unsafe_extraction问题的方法的使用技巧和注意事项,需要的朋友参考一下 在Python项目中运行出现了“AttributeError: ResourceManager instance has no attribute ‘_warn_unsafe_extr