Python多线程爬虫中线程假死问题如何解决?
这几天在搞 boss 直聘,全国数据.
写了个多线程爬虫, 晚上挂到服务器上爬虫速度杠杠滴, 第二天来看爬虫速度巨慢无比,
我是开的 300 线程,
查看服务器状态, cpu 内存, 带宽 非常正常,
然后网上查查资料 可能是 TCP 的端口占用 TIME_WAIT 太多, 导致服务器可用端口耗尽,
但是我测试了一下 10 个线程, 发现还是会变慢,
然后考虑到可能是线程假死了,,
代码大概: requests 请求全部加了 timeout ;
需要对数据库操作的方法都写了对 mysql 的连接; 更新,查找操作都加了线程锁;
while true 循环 break 加好了都
那么问题来了,大佬们,我怎么判断出是哪个步骤假死了,
如果判断不出来, 有没有操作 可以重启这个线程..
或者大佬们有没有遇到过这种情况,求指点
Python多线程爬虫中线程假死问题如何解决?
3 回复
多线程爬虫里线程“假死”通常是因为没处理好网络请求超时、资源竞争或者异常捕获。核心就两点:设置合理的超时和用队列管理任务。
下面这个例子用concurrent.futures的ThreadPoolExecutor,配合队列和超时设置,能有效避免线程卡住:
import concurrent.futures
import requests
import queue
from threading import Lock
import time
class RobustCrawler:
def __init__(self, max_workers=5, timeout=10):
self.timeout = timeout
self.task_queue = queue.Queue()
self.lock = Lock()
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)
def add_task(self, url):
self.task_queue.put(url)
def worker(self):
while True:
try:
url = self.task_queue.get(timeout=3) # 队列获取超时
if url is None:
break
try:
# 关键:设置请求超时
resp = requests.get(url, timeout=self.timeout)
with self.lock:
print(f"Success: {url} - Status: {resp.status_code}")
except requests.exceptions.Timeout:
print(f"Timeout: {url}")
except Exception as e:
print(f"Error: {url} - {str(e)[:50]}")
except queue.Empty:
break
finally:
self.task_queue.task_done()
def run(self, urls):
for url in urls:
self.add_task(url)
# 提交任务
futures = [self.executor.submit(self.worker) for _ in range(self.executor._max_workers)]
# 等待队列清空
self.task_queue.join()
# 停止工作线程
for _ in range(self.executor._max_workers):
self.add_task(None)
concurrent.futures.wait(futures)
self.executor.shutdown()
# 使用示例
if __name__ == "__main__":
urls = [
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/5",
"https://httpbin.org/status/404",
"https://not-a-real-site.com",
"https://httpbin.org/delay/8"
] * 2
crawler = RobustCrawler(max_workers=3, timeout=6)
crawler.run(urls)
关键点:
- 请求超时:
requests.get(timeout=)必须设置,这是防止网络阻塞的核心 - 队列超时:
queue.get(timeout=)避免线程空等 - 异常隔离:每个请求的异常单独捕获,不会影响其他线程
- 优雅退出:用
None作为停止信号,避免线程无法结束
如果要用threading模块直接写,记得给每个线程加daemon=True或者设计明确退出机制。不过现在更推荐用concurrent.futures,它内置了更好的异常处理和线程管理。
简单说就是:设好超时,管好异常,别让一个请求卡死整个程序。
代码呢?

