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

Python OpenCV多进程摄像头程序Ubuntu下资源无法释放问题求助

问题根因说明
  • 为什么Ubuntu下terminate()会导致摄像头资源不释放:Linux内核的V4L2摄像头驱动是通过进程上下文管理缓冲区的,multiprocessing.Process.terminate()会向子进程发送SIGTERM信号强制终止进程,子进程用户态的cap.release()、OpenCV资源析构逻辑完全没有执行机会,内核中持有的V4L2缓冲区锁没有被正常释放,就会触发VIDIOC_QBUF: No buffer space available错误,该锁无法通过普通进程操作清除,必须重启系统。
  • 为什么注释terminate()后程序无法退出:主进程收到q按键后就停止从cam_image_queue消费数据,子进程的cam_image_queue.put(img)是阻塞调用,当队列填满后子进程会永久卡在put操作上,永远无法执行到if not msg_queue.empty()的退出判断逻辑,导致进程无法正常终止。

问题1解决方案:保留cam_process.terminate()调用

修改思路:给子进程留足执行资源释放的时间,发送退出信号后先等子进程主动退出,超时再执行强制终止

修改点:

  1. 发送退出消息后不要立刻停止消费cam_image_queue,清空队列避免子进程卡在put操作
  2. 先调用join设置超时时间等待子进程主动退出,确认子进程已执行完cap.release()后再决定是否需要terminate()

对应修改代码片段:

import time # 新增导入

# main函数内q按键后的逻辑修改为:
if key == ord("q"):
    msg_queue.put("quit")
    # 继续消费队列2秒,让子进程不会卡在put操作上
    start_wait = time.time()
    while time.time() - start_wait < 2:
        if not cam_image_queue.empty():
            cam_image_queue.get()
        # 检查子进程是否已经退出
        if cam_process.exitcode is not None:
            break
        time.sleep(0.01)
    break
# 后面的终止逻辑调整为先等子进程主动退出
cam_process.join(timeout=3)
if cam_process.exitcode is None:
    # 只有子进程超时未退出才强制终止
    cam_process.terminate()
    cam_process.join()

这种修改下,绝大多数场景子进程都会在超时前主动执行完cap.release()后退出,不会触发强制终止,也就不会出现资源泄漏的问题。


问题2解决方案:不使用cam_process.terminate()调用

修改思路:彻底解决子进程阻塞问题,让子进程可以正常收到退出信号并主动退出

修改点:

  1. 给cam_image_queue设置最大长度,避免队列无限制膨胀
  2. 子进程使用非阻塞方式put队列,队列满直接丢帧避免永久阻塞,每次循环优先检查退出信号
  3. 主进程发送退出信号后等待子进程主动退出即可

完整修改后代码:

import multiprocessing
import cv2

def cam_loop(cam_image_queue, msg_queue):
    cap = cv2.VideoCapture(0)
    while True:
        # 优先检查退出信号
        if not msg_queue.empty():
            print("got quit msg")
            break
        hello, img = cap.read()
        if not hello:
            break
        # 非阻塞put,队列满就丢帧,不会卡住
        try:
            cam_image_queue.put(img, block=False)
        except:
            pass
    print(f"before cap release, isOpened={cap.isOpened()}")
    cap.release()
    print(f"after cap release, isOpened={cap.isOpened()}")

def main():
    # 给队列设置最大长度,最多存2帧避免溢出
    cam_image_queue = multiprocessing.Queue(maxsize=2)
    msg_queue = multiprocessing.Queue()
    cam_process = multiprocessing.Process(
        target=cam_loop,
        args=(cam_image_queue, msg_queue,),
    )
    print("starting process...")
    cam_process.start()
    while True:
        if not cam_image_queue.empty():
            img = cam_image_queue.get()
            cv2.imshow("from queue", img)
        key = cv2.waitKey(1)
        if key == ord("q"):
            msg_queue.put("quit")
            break
    print("closing process...")
    # 不需要terminate,等待子进程自己退出即可
    cam_process.join()
    print("after cam_process join")
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

原理说明:

子进程每次循环优先检查退出信号,队列满的时候非阻塞put直接丢帧,不会出现永久阻塞的情况,收到退出信号后立刻执行cap.release()释放摄像头资源然后主动退出,主进程只需要等待子进程退出即可,不需要强制终止,完全避免了资源泄漏的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 10:36:01