您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

探寻python多线程ctrl+c退出问题解决方案

2024/5/12 23:06:48发布27次查看
场景:
经常会遇到下述问题:很多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()一定要放在线程创建前,原因未知。。。。,否则立刻就结束
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product