2012-06-25 91 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