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

Python并行编程:线程进程关闭后主进程仍运行的问题求助

问题:线程/进程关闭后代码仍运行,无法关闭残留线程

问题现象

作为Python并行编程初学者,我遇到线程和进程关闭后代码仍在运行的问题。用以下代码检查存活线程/进程:

print(f"thread : {threading.enumerate()}")
print(f"process : {multiprocessing.active_children()}")

返回结果:

thread : [<_MainThread(MainThread, started 15848)>, <Thread(QueueFeederThread, started daemon 5336)>]
process : []

不知道如何关闭这两个线程,以下是我的代码:

import threading
import multiprocessing
import queue
import torch
import cv2
import time


class read_video(threading.Thread):
    def __init__(self, cv, lock, sync_lock, display_frame_queue, predict_frame_queue):
        super().__init__(name="SHOW_VIDEO")
        self.daemon = True
        self.cv = cv2.VideoCapture(cv)
        self.display_frame_queue = display_frame_queue
        self.lock = lock
        self.sync_lock = sync_lock
        self.predict_frame_queue = predict_frame_queue

    def read(self):

        self.sync_lock.wait()
        while True:
            if self.display_frame_queue.qsize() <= 3:
                ret, frame = self.cv.read()
                if ret:
                    frame = cv2.resize(frame, (1280, 720))
                    self.display_frame_queue.put(frame)
                    self.predict_frame_queue.put(frame)
                else:
                    break
            if self.lock.is_set():
                self.cv.release()
                break

        return

    def run(self):
        self.read()


class display_video(threading.Thread):
    def __init__(self, lock, sync_lock, display_frame_queue, predict_result_queue):
        super().__init__(name="READ_VIDEO")
        self.lock = lock
        self.daemon = True
        self.sync_lock = sync_lock
        self.display_frame_queue = display_frame_queue
        self.predict_result_queue = predict_result_queue

    def show(self):

        color = [(0, 255, 0), (255, 255, 0), (0, 255, 255), (0, 140, 255)]
        class_name = ['Car', 'Motorcycle', 'Person', 'Truck']
        self.sync_lock.wait()
        while True:
            frame = self.display_frame_queue.get()
            result = self.predict_result_queue.get()
            for i in result:
                object_type = int(i[-1])
                cv2.rectangle(frame, (int(i[0]), int(i[1])), (int(i[2]), int(i[3])), color[object_type], 1)
                cv2.putText(frame, f"{format(i[-2], '.2f')} {class_name[object_type]}", (int(i[0]), int(i[1] - 10)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.6, color[object_type], 2)
            cv2.imshow("vnaoisdf", frame)
            if cv2.waitKey(1) & 0xff == ord('c'):
                self.lock.set()
                cv2.destroyAllWindows()
                break

        return

    def run(self):
        self.show()


class Model(multiprocessing.Process):
    def __init__(self, model_path, lock, sync_lock, predict_frame_queue, predict_result_queue):
        super().__init__(name="MODEL")
        self.daemon = True
        self.model_path = model_path
        self.lock = lock
        self.sync_lock = sync_lock
        self.predict_frame_queue = predict_frame_queue
        self.predict_result_queue = predict_result_queue

    def predict(self):

        model = torch.hub.load('ultralytics/yolov5', 'custom', path=self.model_path)
        model.cuda()
        self.sync_lock.wait()
        while True:
            if not self.predict_frame_queue.empty():
                frame = self.predict_frame_queue.get()
                predict = model(frame).xyxy[0].cpu().numpy()
                self.predict_result_queue.put(predict)
            if self.lock.is_set():
                break
        self.close()

        return

    def run(self):
        self.predict()


if __name__ == "__main__":
    lock = multiprocessing.Event()
    sync_lock = multiprocessing.Barrier(3)
    display_queue = queue.Queue()
    predict_frame_queue = multiprocessing.Queue()
    predict_result_queue = multiprocessing.Queue()

    a = read_video("testingv.mp4", lock, sync_lock, display_queue, predict_frame_queue)
    b = display_video(lock, sync_lock, display_queue, predict_result_queue)
    c = Model("best200.pt", lock, sync_lock, predict_frame_queue, predict_result_queue)
    a.start()
    b.start()
    c.start()

    a.join()
    b.join()
    c.join()

    predict_frame_queue.close()
    predict_result_queue.close()
    c.terminate()

    print(f"thread : {threading.enumerate()}")
    print(f"process : {multiprocessing.active_children()}")

问题原因与解决方法

1. 关于QueueFeederThread

这个线程是multiprocessing.Queue内部自动创建的守护线程,专门处理进程间队列的数据传输。它的属性是守护线程,意味着只要主线程结束,它会自动退出,不需要手动关闭。你看到它存活,是因为主线程还在执行收尾逻辑,程序最终会自动终止。

2. 主线程(_MainThread)无法正常结束的核心问题

主线程是程序本身,只有所有非守护线程结束后才会退出。你的代码存在几个阻塞线程退出的问题:

问题点1:队列get()阻塞卡死

display_video的show()方法中,frame = self.display_frame_queue.get()和result = self.predict_result_queue.get()是阻塞调用——如果队列空了,线程会一直卡在这,即使触发lock信号也无法执行break。

解决:给get()添加超时,或者先检查终止信号再取队列:

# 修改display_video的show方法循环部分
while True:
    # 优先检查终止信号
    if self.lock.is_set():
        cv2.destroyAllWindows()
        break
    # 非阻塞获取队列,避免卡死
    try:
        frame = self.display_frame_queue.get(timeout=0.1)
        result = self.predict_result_queue.get(timeout=0.1)
    except queue.Empty:
        continue
    # 后续绘制代码不变

问题点2:multiprocessing.Queue未完成清理

你调用了close()但没调用join_thread(),无法等待队列后台线程完成数据清理,会导致主线程被挂起。

解决:关闭队列后添加join_thread():

predict_frame_queue.close()
predict_frame_queue.join_thread()
predict_result_queue.close()
predict_result_queue.join_thread()

问题点3:Model进程中不存在close()方法

Model的predict()方法里调用了self.close(),但这个方法没有定义,会抛出异常导致进程无法正常退出。

解决:删除self.close(),如果需要清理模型,手动添加释放代码:

# 修改Model的predict方法
def predict(self):
    model = torch.hub.load('ultralytics/yolov5', 'custom', path=self.model_path)
    model.cuda()
    self.sync_lock.wait()
    while True:
        if not self.predict_frame_queue.empty():
            frame = self.predict_frame_queue.get()
            predict = model(frame).xyxy[0].cpu().numpy()
            self.predict_result_queue.put(predict)
        if self.lock.is_set():
            # 清理模型和GPU缓存
            del model
            torch.cuda.empty_cache()
            break
    # 去掉self.close()
    return

问题点4:read_video无法及时响应终止信号

read_video的循环先检查队列大小,再读取视频,最后才检查lock——如果队列一直大于3,会卡在视频读取循环里,无法及时响应终止信号。

解决:把lock检查放到循环最开头:

# 修改read_video的read方法循环
while True:
    # 优先检查终止信号
    if self.lock.is_set():
        self.cv.release()
        break
    if self.display_frame_queue.qsize() <= 3:
        ret, frame = self.cv.read()
        if ret:
            frame = cv2.resize(frame, (1280, 720))
            self.display_frame_queue.put(frame)
            self.predict_frame_queue.put(frame)
        else:
            self.cv.release()
            break

最终效果

修改后,按下c触发lock.set(),所有线程/进程都会及时检测到信号并退出,队列完成清理后主线程会正常结束,QueueFeederThread也会随主线程自动退出,程序完全终止。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 20:12:06