You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python ThreadPoolExecutor多线程并发写同文件内容错乱问题

问题根因

多线程并发场景下,文件写入操作不是原子操作,多个线程同时抢占文件IO资源执行写入时,不同线程的写入内容会互相打断,最终出现内容交错混杂的问题。除此之外,代码中count_requests、host_error两个全局计数变量的修改也没有做线程安全保护,高并发下会出现计数不准的问题,且原有写入逻辑未拼接原始URL、未加换行符,本身也不符合预期输出格式要求。

可行解决方案
  • 方案1:加线程锁
    引入threading.Lock线程同步锁,所有线程在执行文件写入、修改全局计数操作前必须先获取锁,操作完成后释放锁,保证同一时间点只有一个线程执行上述敏感操作,避免竞争。该方案实现简单,但写入逻辑会退化为串行,高并发下锁等待会带来一定性能开销。
  • 方案2:主线程统一写入(推荐)
    子线程仅负责发起HTTP请求、解析页面标题,不直接操作文件、不修改全局变量,将解析结果通过返回值传回主线程;主线程统一收集所有子线程的执行结果,集中完成计数、文件写入、进度打印操作。该方案完全规避多线程资源竞争问题,不需要加锁,逻辑更清晰可维护。
推荐方案参考实现代码
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import fileinput
from bs4 import BeautifulSoup
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import warnings

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

def get_url(url):
    url = url.strip()
    try:
        result_request = requests.get(url, verify=False, timeout=10)
        soup = BeautifulSoup(result_request.text, 'html.parser')
        title = soup.title.get_text(strip=True)
        return (True, url, title)
    except Exception as e:
        return (False, url, str(e))

if __name__ == "__main__":
    count_requests = 0
    host_error = 0
    # 提前清空输出文件
    with open('outfile.txt', 'w', encoding="utf-8") as f:
        pass
    # 创建线程池
    with ThreadPoolExecutor(max_workers=100) as pool:
        all_task = []
        # 提交所有任务
        for line in fileinput.input(['urls.txt']):
            if line.strip():
                all_task.append(pool.submit(get_url, line))
        # 逐次收集已完成的任务结果,统一写入
        for future in as_completed(all_task):
            is_success, url, content = future.result()
            if is_success:
                count_requests += 1
                with open('outfile.txt', 'a', encoding="utf-8") as f:
                    f.write(f"{url} - {content}\n")
            else:
                host_error += 1
            # 实时打印真实进度
            print(f"requests success : {count_requests} | requests error {host_error}", end='\r')
补充说明

如果坚持使用子线程写入的方案,只需要在全局初始化锁对象write_lock = threading.Lock(),将文件写入、计数修改的逻辑放到with write_lock:上下文块中即可,注意锁的范围不要覆盖HTTP请求、页面解析等耗时逻辑,避免不必要的性能损耗。

内容的提问来源于stack exchange,提问作者yand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:30:36