2012-06-25 90 views
3

我想知道如何使用CTRL + C或類似的方法在控制檯中停止我的程序。 問題是我的程序中有兩個線程。線程抓取Web並提取一些數據,並且線程2以可讀格式爲用戶顯示此數據。兩部分共享相同的數據庫。我這樣運行它們:Python如何停止線程操作

from threading import Thread 
import ResultsPresenter 

def runSpider(): 
    Thread(target=initSpider).start() 
    Thread(target=ResultsPresenter.runPresenter).start() 


if __name__ == "__main__": 
    runSpider() 

我該怎麼做?

好了,所以我創建了自己的線程類:

import threading 

class MyThread(threading.Thread): 
"""Thread class with a stop() method. The thread itself has to check 
regularly for the stopped() condition.""" 

def __init__(self): 
    super(MyThread, self).__init__() 
    self._stop = threading.Event() 

def stop(self): 
    self._stop.set() 

def stopped(self): 
    return self._stop.isSet() 

行,所以我會在這裏發佈resultPresenter和履帶式的片段。 這裏是resultPresenter的代碼:

# configuration 
DEBUG = False 
DATABASE = database.__path__[0] + '/database.db' 

app = Flask(__name__) 
app.config.from_object(__name__) 
app.config.from_envvar('CRAWLER_SETTINGS', silent=True) 

def runPresenter():  
    url = "http://127.0.0.1:5000" 
    webbrowser.open_new(url) 
    app.run() 

也有此兩種方法,我省略了 - 他們中的一個連接到數據庫,第二種方法加載HTML模板來顯示結果。我重複這一點,直到條件滿足或用戶停止該程序(我試圖實現)。還有兩個其他方法 - 一個是從命令行獲得的初始鏈接,另一個是第二個參數 - 如果參數無效,我將不運行crawl()方法。

這裏是履帶式的短版:

def crawl(initialLink, maxDepth): 
#here I am setting initial values, lists etc 

while not(depth >= maxDepth or len(pagesToCrawl) <= 0): 

    #this is the main loop that stops when certain depth is 
    #reached or there is nothing to crawl 
    #Here I am popping urls from url queue, parse them and 
    #insert interesting data into the database 


parser.close() 
sock.close()    
dataManager.closeConnection() 

這裏是初始化文件,開始在線程這些模塊:

import ResultsPresenter, MyThread, time, threading 

def runSpider(): 

    MyThread.MyThread(target=initSpider).start() 
    MyThread.MyThread(target=ResultsPresenter.runPresenter).start() 


def initSpider(): 

    import Crawler 
     import database.__init__ 
    import schemas.__init__ 
    import static.__init__ 
    import templates.__init__ 

    link, maxDepth = Crawler.getInitialLink() 
    if link: 
     Crawler.crawl(link, maxDepth) 



killall = False 

if __name__ == "__main__":  

global killall 
runSpider() 

while True: 

    try: 
     time.sleep(1) 

    except:    

     for thread in threading.enumerate(): 
      thread.stop() 

     killall = True 
     raise 

回答

3

殺死線程是不是一個好主意,因爲(作爲你已經說過)他們可能會在數據庫上執行一些關鍵的操作。因此,你可以定義全局標誌,這將表示它們應該完成他們正在做的和退出的線程。

killall = False 

import time 
if __name__ == "__main__": 
    global killall 
    runSpider() 
    while True: 
     try: 
      time.sleep(1) 
     except: 
      /* send a signal to threads, for example: */ 
      killall = True 
      raise 

和你類似的循環killall變量是否被設置爲True檢查每個線程。如果它關閉所有活動並退出線程。

編輯

首先:唯一的例外是相當明顯的。您將target參數傳遞給__init__,但您沒有在__init__中聲明它。這樣做:

class MyThread(threading.Thread): 

    def __init__(self, *args, **kwargs): 
     super(MyThread, self).__init__(*args, **kwargs) 
     self._stop = threading.Event() 

第二:你沒有使用我的代碼。正如我所說:設置標誌並在線程中檢查它。當我說「線程」時,我實際上是指處理程序,即ResultsPresenter.runPresenterinitSpide。向我們展示其中一個代碼,我將嘗試向您展示如何處理停止。

EDIT 2

假設crawl函數的代碼是在同一個文件(如果沒有,那麼你必須輸入killall變量),你可以做這樣的事情

def crawl(initialLink, maxDepth): 
    global killall 
    # Initialization. 
    while not killall and not(depth >= maxDepth or len(pagesToCrawl) <= 0): 
     # note the killall variable in while loop! 
     # the other code 
    parser.close() 
    sock.close()    
    dataManager.closeConnection() 

所以基本上你只是說:「嘿,線程,現在退出循環!」。您也可以選擇從字面上打破循環:

while not(depth >= maxDepth or len(pagesToCrawl) <= 0): 
    # some code 
    if killall: 
     break 

當然,它仍然需要一段時間它退出之前(已完成循環並關閉解析器,插座等),但它應該退出安全。至少這是這個想法。

+0

這似乎是我正在尋找的解決方案,但我仍然不知道如何攔截來自命令行的信號以及如何阻止這兩種方式代碼中的ds。你能舉一些例子嗎?無論如何謝謝 – koleS

+0

這將工作。 'try:except:'沒有指定'Exception'的塊實際上會捕獲異常異常(如CTRL + C)。這就是爲什麼我要「睡覺」(它什麼都不做,但我們希望不斷檢查)。至於停止線程:這取決於它們如何實現。 :) – freakish

+0

實際上它不工作 - 推CTRL + C鍵後,引發KeyInterrupt,但兩個模塊仍在運行。順便說一句。當你應用於變量時,你能解釋'全局'的含義嗎?它是由我的數據包中的所有模塊看到的還是什麼? – koleS

1

試試這個:

ps aux | grep python 

複製要終止的進程的ID和:

kill -3 <process_id> 

而在你的代碼(改編自here):

import signal 
import sys 
def signal_handler(signal, frame): 
     print 'You killed me!' 
     sys.exit(0) 
signal.signal(signal.SIGQUIT, signal_handler) 
print 'Kill me now' 
signal.pause() 
+0

這可能是好的,但我得到以下錯誤: signal.signal(signal.SIGKILL,signal_handler) RuntimeError:(22,'無效參數') – koleS

+0

已更新。現在它對我來說工作正常 – luke14free

+0

仍然不適合我或我不知道如何使用它,你可以提供一些更詳細的例子嗎? – koleS