探尋python多線程ctrl+c退出問題解決方案

來源:互聯網
上載者:User
情境:

經常會遇到下述問題:很多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()一定要放線上程建立前,原因未知。。。。,否則立刻就結束

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.