探寻python多线程ctrl+c退出问题解决方案
Jun 06, 2016 am 11:19 AM场景:
经常会遇到下述问题:很多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()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

Article chaud

Outils chauds Tags

Article chaud

Tags d'article chaud

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Google AI annonce Gemini 1.5 Pro et Gemma 2 pour les développeurs

Comment gérer les ressources partagées en multi-threading en C++ ?

Défis et contre-mesures de la gestion de la mémoire C++ dans un environnement multithread ?

Défis et stratégies pour tester les programmes multithread en C++
