Rumah pembangunan bahagian belakang Tutorial Python 探寻python多线程ctrl+c退出问题解决方案

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

Jun 06, 2016 am 11:19 AM
ctrl+c python multithreading

场景:

经常会遇到下述问题:很多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()一定要放在线程创建前,原因未知。。。。,否则立刻就结束

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Tag artikel panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Cara Muat turun DeepSeek Xiaomi Cara Muat turun DeepSeek Xiaomi Feb 19, 2025 pm 05:27 PM

Cara Muat turun DeepSeek Xiaomi

Google AI mengumumkan Gemini 1.5 Pro dan Gemma 2 untuk pembangun Google AI mengumumkan Gemini 1.5 Pro dan Gemma 2 untuk pembangun Jul 01, 2024 am 07:22 AM

Google AI mengumumkan Gemini 1.5 Pro dan Gemma 2 untuk pembangun

Bagaimana untuk menangani sumber yang dikongsi dalam multi-threading dalam C++? Bagaimana untuk menangani sumber yang dikongsi dalam multi-threading dalam C++? Jun 03, 2024 am 10:28 AM

Bagaimana untuk menangani sumber yang dikongsi dalam multi-threading dalam C++?

Bagaimana anda bertanya kepadanya Deepseek Bagaimana anda bertanya kepadanya Deepseek Feb 19, 2025 pm 04:42 PM

Bagaimana anda bertanya kepadanya Deepseek

Cara Mencari DeepSeek Cara Mencari DeepSeek Feb 19, 2025 pm 05:18 PM

Cara Mencari DeepSeek

Cabaran dan tindakan balas pengurusan memori C++ dalam persekitaran berbilang benang? Cabaran dan tindakan balas pengurusan memori C++ dalam persekitaran berbilang benang? Jun 05, 2024 pm 01:08 PM

Cabaran dan tindakan balas pengurusan memori C++ dalam persekitaran berbilang benang?

Cabaran dan strategi untuk menguji program berbilang benang dalam C++ Cabaran dan strategi untuk menguji program berbilang benang dalam C++ May 31, 2024 pm 06:34 PM

Cabaran dan strategi untuk menguji program berbilang benang dalam C++

Cara Program DeepSeek Cara Program DeepSeek Feb 19, 2025 pm 05:36 PM

Cara Program DeepSeek

See all articles