python3中断多线程的合适方法

python3中断多线程的合适方法,python,multithreading,python-3.x,Python,Multithreading,Python 3.x,我正在编写一个多线程网络爬虫,每个线程的例程函数基本上是一个无止境的循环,有一个共享字典来存储那些已经访问过的url,我使用线程锁来同步 有时我可能想用ctrl+c来中断它,我在线程运行函数中添加了一个try-catch来捕获键盘中断,然后我可能想做一些最终的任务,例如将数据转储到数据库或pkl文件 问题是,每次我打断它,它就无法进入捕获过程,有时它只是停留在那里,有时它仍然在运行 那么,在多线程编程中处理异常/中断的最佳实践是什么呢 我的代码如下: from bs4 import Beauti

我正在编写一个多线程网络爬虫,每个线程的例程函数基本上是一个无止境的循环,有一个共享字典来存储那些已经访问过的url,我使用线程锁来同步

有时我可能想用ctrl+c来中断它,我在线程运行函数中添加了一个try-catch来捕获键盘中断,然后我可能想做一些最终的任务,例如将数据转储到数据库或pkl文件

问题是,每次我打断它,它就无法进入捕获过程,有时它只是停留在那里,有时它仍然在运行

那么,在多线程编程中处理异常/中断的最佳实践是什么呢

我的代码如下:

from bs4 import BeautifulSoup
import requests
import threading
import queue
import pickle
import os
from concurrent.futures import ThreadPoolExecutor

worker_num = 8
q = queue.Queue()
lock = threading.Lock()

if os.path.exists('./checked.pkl'):
    with open('./checked.pkl', 'rb') as f:
        checked = pickle.load(f)
else:
    checked = set()


def get_links(url):
    # do sth....


def run():
    print(threading.current_thread())
    try:
        while True:
            next_url = q.get()
            links = get_links(next_url)
            lock.acquire()
            for link in links:
                if link not in checked:
                    q.put(link)
            print(len(checked))
            lock.release()
    except Exception as e:
        print(e)
        print('interrupt')
        lock.acquire()
        with open('./checked.pkl', 'wb') as f:
            pickle.dump(checked, f)
        lock.release()


if __name__ == '__main__':
    q.put(start_url)
    with ThreadPoolExecutor(worker_num) as executor:
        for _ in range(worker_num):
            executor.submit(run)

键盘中断始终在主线程中引发。您无法在其他线程中处理它

您应该在每个成功的工作块之后检查您的状态(不必在每个URL之后,但也可以在每个URL之后)。这将为您提供状态更新,即使您的进程因其他原因崩溃(某些不可处理的原因,如segfault或整个主机崩溃)。您还应该以原子方式编写检查点,这样,如果在更新过程中崩溃,就不会导致损坏、无法使用的状态。(由于pickle的不安全性、脆弱性和跨语言挑战,也不应将其用作检查点状态格式。)

一旦在正常操作期间安全且定期地写入状态更新,就可以通过将工作线程设置为守护进程线程并向主线程添加
KeyboardInterrupt
处理来使爬虫程序可中断。在这一点上,如果工作线程在没有清理机会的情况下被杀死(如果您让它们成为守护线程并让主线程退出,就会发生这种情况),这无关紧要,因为它们最近已经检查了它们的状态


此外,您应该考虑使用现有的爬行工具,而不是滚动自己。

<代码>键盘中断> /COD>总是在主线程中引发。您无法在其他线程中处理它

您应该在每个成功的工作块之后检查您的状态(不必在每个URL之后,但也可以在每个URL之后)。这将为您提供状态更新,即使您的进程因其他原因崩溃(某些不可处理的原因,如segfault或整个主机崩溃)。您还应该以原子方式编写检查点,这样,如果在更新过程中崩溃,就不会导致损坏、无法使用的状态。(由于pickle的不安全性、脆弱性和跨语言挑战,也不应将其用作检查点状态格式。)

一旦在正常操作期间安全且定期地写入状态更新,就可以通过将工作线程设置为守护进程线程并向主线程添加
KeyboardInterrupt
处理来使爬虫程序可中断。在这一点上,如果工作线程在没有清理机会的情况下被杀死(如果您让它们成为守护线程并让主线程退出,就会发生这种情况),这无关紧要,因为它们最近已经检查了它们的状态

此外,您应该考虑使用现有的爬行工具,而不是滚动自己。