Flask多线程站点标题抓取项目如何实现一次性终止所有线程
问题根因
你现有代码的终止标识仅在循环遍历域名的阶段检查,当程序已经进入get_title执行网络请求时,不会触发终止逻辑,必须等当前请求完全执行完毕、进入下一轮循环时才会检测到stop_threads为True,所以会出现要等当前任务跑完才会停的问题。
Python官方没有提供线程强制终止的API,强行杀线程可能会导致内存泄漏、资源不释放的问题,用multiprocessing确实可以解决这个问题,因为进程支持直接调用terminate()方法强制终止,不需要等待内部逻辑执行完成。
优化后的多进程版本代码
import requests, time from bs4 import BeautifulSoup from flask import Flask, render_template, request, redirect, url_for import multiprocessing # 存储所有工作进程的全局列表 process_list = [] def chunker_list(seq, size): return (seq[i::size] for i in range(size)) def get_domens_from_file(filename): with open(filename, 'r') as f: domens = f.readlines() return domens def get_title(url): # 给请求加超时,避免单个请求卡住过久 response = requests.get(url, timeout=10) soup = BeautifulSoup(response.text, 'html.parser') title = soup.find('title').text.replace('\n', '').replace(' ', '') return title def start_work(domens): for domen in domens: title = get_title(domen.strip()) print(title) time.sleep(1) app = Flask(__name__) @app.route('/', methods=['POST', 'GET']) def main(): global process_list if request.method == 'POST': index = request.form['index'] if index == 'start': # 先清空之前的进程列表,避免重复终止 process_list = [] domens = get_domens_from_file('C:\\Users\\Admin\\Desktop\\domains.txt') domens_list = list(chunker_list(domens, 5)) for domens in domens_list: p = multiprocessing.Process(target = start_work, args=(domens, )) p.start() process_list.append(p) if index == 'stop': # 遍历所有进程直接强制终止 for p in process_list: if p.is_alive(): p.terminate() p.join() process_list = [] print('stop') return redirect(url_for('main')) return render_template('index.html') if __name__ == '__main__': # 多进程场景必须关闭debug模式,否则自动重载会导致进程管理异常 app.run(debug=False)
注意事项
- 多进程场景下不要开启Flask的debug模式,debug模式的自动重载机制会导致进程实例重复生成,无法正常终止
- 如果不想换多进程,也可以在线程版本里给
requests.get加超时,同时把stop_threads改成threading.Event对象,检查的颗粒度更细,也能大幅降低终止延迟,只是无法做到立刻终止当前正在执行的请求 - 全局变量的读写在多线程场景下建议加锁,避免多线程同时修改变量出现可见性问题
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

