Python gstreamer 使用中的多余线程

1 投票
1 回答
2145 浏览
提问于 2025-05-01 02:37

我注意到在用Python使用gstreamer后(我用的是Ubuntu 14.04的python-gst-1.0 deb包版本1.2.0-1),每次运行编码器时似乎都会产生一个多余的线程。我在一个我写的模块里使用gstreamer接口,并且在这个模块里运行gobject.mainloop,并且调用了mainloop.quit(),所以我不认为问题出在主循环上。

经过几次运行,使用threading.enumerate()查看线程情况时,显示:

[<_MainThread(MainThread, started 140079923849024)>,
 <_DummyThread(Dummy-1, started daemon 140079768815360)>,
 <_DummyThread(Dummy-3, started daemon 140079785338624)>,
 <_DummyThread(Dummy-4, started daemon 140079418832640)>,
 <_DummyThread(Dummy-2, started daemon 140079802386176)>]

幸运的是,这些线程都是以守护线程的方式启动的,所以程序会退出,但我不知道怎么清理这些线程。它们影响了我使用Ctrl-C退出脚本的可能性,因为KeyboardInterrupt似乎并不总是能传递到主线程。我结束运行循环的方式是:

try:
    time.sleep(899.0)
except KeyboardInterrupt:
    pass

time.sleep(1.0)

这样我可以通过快速按两次Ctrl-C来中断循环的超时,第一次会捕获try/except,第二次在1秒的休眠上没有处理器,因此会退出。然而,由于这些多余的线程,第二次Ctrl-C在这个层面上似乎从未被看到,所以我需要按Ctrl-Z回到命令行,然后强制结束脚本。我对此很不满意。

有没有人知道这个多余的线程是什么,怎么让它配合并结束?我准备在一个正在运行的进程上使用gdb来确定它可能是什么。

这个类的代码(简化了不相关的部分):

class GstEncoder:
    def __init__(self, metadata, mediainfo):
        self.error = None

        # used for controlling logic which I removed for clarity
        self.metadata = metadata
        self.mediainfo = mediainfo

        # Create a pipeline in self.pipeline
        self.setupPipeline()

        # Put in the MainLoop
        self.mainloop = GObject.MainLoop()
        self.context = self.mainloop.get_context()
        self.abort = False

    def __del__(self):
        logger.info("Dying gasp!")
        if self.mainloop.is_running():
            self.mainloop.quit()
        self.pipeline.unref()

    def start(self):
        # Set in playing mode
        self.pipeline.set_state(Gst.State.PLAYING)

        # actually only used in some situations, removed the controlling logic for clarity
        GObject.timeout_add_seconds(900, self.timedOut)
        GObject.timeout_add_seconds(30, self.progressReport)

        try:
            self.abort = False
            self.mainloop.run()
        except KeyboardInterrupt:
            logger.warning("Aborted by Ctrl-C")
            self.abort = True
            self.mainloop.quit()
            raise KeyboardInterrupt

        # Stop the pipeline
        self.pipeline.set_state(Gst.State.NULL)
        return self.error

    def progressReport(self):
        position = self.pipeline.query_position(Gst.Format.TIME)[1]
        duration = self.pipeline.query_duration(Gst.Format.TIME)[1]
        if self.abort:
            return False
        percentage = 0.0 if duration == 0 \
                         else float(position) / float(duration) * 100.0
        logger.info("Progress: %s / %s  (%.2f%%)" % (Gst.TIME_ARGS(position),
                Gst.TIME_ARGS(duration), percentage))
        return True

    def timedOut(self):
        if self.abort:
            return False
        self.error = "Aborted by watchdog timer"
        logger.warning(self.error)
        self.abort = True
        self.mainloop.quit()
        return False

这个类是这样实例化的:

err = None
# Filename, etc is in metadata
encoder = GstEncoder(metadata, mediainfo)
if encoder.error:
    err = encoder.error
if not err:
    err = encoder.start()

if err:
    logger.error(err)

encoder = None
print threading.enumerate()

一个示例管道可以在这里看到:https://s3.amazonaws.com/beirdo-share/before.png

暂无标签

1 个回答

1

我遇到了一个类似的问题:按下 Ctrl-C 会触发一个 KeyboardInterrupt,但是程序并没有退出,因为有一些非守护线程在运行。解决这个问题的方法是在应用程序启动时调用一次 GObject.threads_init()

撰写回答