python c语言 多线程,python多线程ctrl-c退出问题(转)

场景:

经常会遇到下述问题:很多io

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

Java代码 a4c26d1e5885305701be709a3d33442f.png

publicclassTest {

publicstaticvoidmain(String[] args)throwsException {

newThread(newRunnable() {

publicvoidrun() {

longstart = System.currentTimeMillis();

while(true) {

try{

Thread.sleep(1000);

}catch(Exception e) {

}

System.out.println(System.currentTimeMillis());

if(System.currentTimeMillis() - start >1000*100)break;

}

}

}).start();

}

}

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代码:

Python代码 a4c26d1e5885305701be709a3d33442f.png

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

importtime

importthreading

start=time.time()

defforeverLoop():

start=time.time()

while1:

time.sleep(1)

printtime.time()

iftime.time()-start>100:

break

thread_=threading.Thread(target=foreverLoop)

#thread_.setDaemon(True)

thread_.start()

# -*- 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模块验证一下:

Python代码 a4c26d1e5885305701be709a3d33442f.png

defsigint_handler(signum,frame):

print"main-thread exit"

sys.exit()

signal.signal(signal.SIGINT,sigint_handler)

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

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

Python代码 a4c26d1e5885305701be709a3d33442f.png

_shutdown = _MainThread()._exitfunc

def_exitfunc(self):

self._Thread__stop()

t = _pickSomeNonDaemonThread()

ift:

if__debug__:

self._note("%s: waiting for other threads",self)

whilet:

t.join()

t = _pickSomeNonDaemonThread()

if__debug__:

self._note("%s: exiting",self)

self._Thread__delete()

_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吧:

Python代码 a4c26d1e5885305701be709a3d33442f.png

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

importtime,signal,traceback

importsys

importthreading

start=time.time()

defforeverLoop():

start=time.time()

while1:

time.sleep(1)

printtime.time()

iftime.time()-start>5:

break

thread_=threading.Thread(target=foreverLoop)

thread_.setDaemon(True)

thread_.start()

#主线程wait住了,不能接受信号了

#thread_.join()

def_exitCheckfunc():

print"ok"

try:

while1:

alive=False

ifthread_.isAlive():

alive=True

ifnotalive:

break

time.sleep(1)

#为了使得统计时间能够运行,要捕捉 KeyboardInterrupt :ctrl-c

exceptKeyboardInterrupt, e:

traceback.print_exc()

print"consume time :",time.time()-start

threading._shutdown=_exitCheckfunc

# -*- 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.

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

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

Python代码 a4c26d1e5885305701be709a3d33442f.png

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

importtime,signal,traceback,os

importsys

importthreading

start=time.time()

defforeverLoop():

start=time.time()

while1:

time.sleep(1)

printtime.time()

iftime.time()-start>5:

break

classWatcher:

"""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()

ifself.child ==0:

return

else:

self.watch()

defwatch(self):

try:

os.wait()

exceptKeyboardInterrupt:

# I put the capital B in KeyBoardInterrupt so I can

# tell when the Watcher gets the SIGINT

print'KeyBoardInterrupt'

self.kill()

sys.exit()

defkill(self):

try:

os.kill(self.child, signal.SIGKILL)

exceptOSError:pass

Watcher()

thread_=threading.Thread(target=foreverLoop)

thread_.start()

# -*- 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()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值