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

Python多线程处理网页截图调用join后程序冻结如何解决

问题根因

  • 第一,PhotoImage 是GUI框架(大概率是Tkinter)的组件,所有GUI相关的操作都只能在主线程执行,你把self.graph = PhotoImage(...)放在子线程的takeScreenshot里运行,是程序冻结的核心原因,和join操作本身无关。
  • 第二,你10个线程都读写同一个screenshot.png文件,存在严重的文件读写冲突,很容易出现读半截文件、内容被覆盖的问题,哪怕你看到文件存在,实际也可能是其他线程生成的不完整文件。
  • 第三,你的线程传参写法有问题,threading.Thread的args要求传入元组,你写args = webpage会把字符串拆成单个字符逐个传参,takeScreenshot接收参数会直接报错。

修复方案

第一步:调整截图函数逻辑,子线程不碰GUI操作,每个线程生成独立的截图文件

把所有GUI相关代码(包括self.graph = PhotoImage(...))全部挪到主线程,子线程只负责截图、保存文件这类IO密集型操作,同时给每个线程的截图设置唯一文件名避免覆盖:

import threading
import os
from PIL import Image
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

# 子线程仅负责截图,返回生成的截图路径,不操作任何GUI组件
def takeScreenshot(webpage, save_path):
    ublockPath = r'C:\Users\Bassel Attia\Documents\Trading Core\1.37.2_0'
    chromeOptions = Options()
    chromeOptions.add_argument("--log-level=3")
    chromeOptions.add_argument('load-extension=' + ublockPath)
    # 每个线程单独创建driver,不要多线程共用driver
    driver = webdriver.Chrome(ChromeDriverManager().install(), chrome_options=chromeOptions)   
    driver.get(webpage)
    driver.get_screenshot_as_file(save_path)
    image = Image.open(save_path)
    # 裁剪截图
    area = (20, 290, 1250, 800)
    croppedImage=image.crop(area)
    croppedImage.save(save_path)
    image = Image.open(save_path)
    # 调整截图大小
    newHeight = 300
    newWidth = int(newHeight / image.height * image.width)
    resizedImage = image.resize((newWidth, newHeight))
    resizedImage.save(save_path)
    image.close()
    driver.quit()
    return save_path

第二步:修改主线程逻辑,单独开监控线程等待所有任务完成

你需要输入过程不阻塞,输完10个地址后等待所有截图完成再展示,就不要在输入循环里直接join,单独开一个监控线程等待所有截图线程结束,结束后再通知主线程更新GUI,不会卡主程序:

threads=[]
save_paths = []
n=0
while n<10:
    webpage = input("Enter the webpage")
    # 每个截图生成独立文件
    save_path = f"screenshot_{n}.png"
    save_paths.append(save_path)
    # args参数必须传元组,单元素后面加逗号才是元组格式
    thread = threading.Thread(target = takeScreenshot, args = (webpage, save_path))
    thread.start()
    threads.append(thread)
    n +=1

# 监控线程专门等待所有截图任务完成,不阻塞主线程
def wait_all_done():
    for t in threads:
        t.join()
    # 所有截图完成后通知主线程执行展示逻辑,不要在这个线程里操作GUI
    # 如果用Tkinter可以用after方法通知主线程更新:root.after(0, show_all_screenshots, save_paths)
    show_all_screenshots(save_paths)

monitor_thread = threading.Thread(target=wait_all_done)
monitor_thread.start()

# 截图展示逻辑全部放在主线程执行
def show_all_screenshots(paths):
    for p in paths:
        self.graph = PhotoImage(file=p)
        # 剩下的图片展示逻辑写在这里

额外注意事项

  • 所有GUI组件的创建、修改、赋值操作,必须全部放在主线程执行,子线程只能处理截图、下载这类IO操作。
  • 如果用的是Tkinter框架,PhotoImage对象要保留全局引用,不然会被垃圾回收导致图片显示空白。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:09:00