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

GObject信号连接与断开的线程安全性疑问(GStreamer场景)

GObject信号断开与线程安全问题(GStreamer RTSP场景)

示例代码

class CGstreamerStream
{
public:
    void Configure();
    
    ~CGstreamerStream();
    
private:
    static void MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData );
    
    GstRTSPMediaFactory* pFactory = nullptr;
    gulong m_nMediaConfigureHandlerId = 0;
};


void CGstreamerStream::Configure()
{
    // 假设pFactory已在构造函数中初始化
    
    m_nMediaConfigureHandlerId = g_signal_connect( pFactory, "media-configure",
        (GCallback)CGstreamerStream::MediaConfigure, this );
}

CGstreamerStream::~CGstreamerStream()
{
    if( m_nMediaConfigureHandlerId )
    {
        g_signal_handler_disconnect( pFactory, m_nMediaConfigureHandlerId );
    }
}

void CGstreamerStream::MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData )
{
    // 可能由某GStreamer线程调用
    
    CGstreamerStream* self = (CGstreamerStream*)pUserData;
    // 在此使用类成员
    //...
}

问题描述

在上述管理RTSP服务器媒体工厂的C++类实现中,Configure()方法通过g_signal_connect订阅media-configure信号,将类的静态成员函数作为回调处理函数并传入当前实例的this指针;析构函数中调用g_signal_handler_disconnect断开信号连接。

核心疑问:当GStreamer自有线程正在执行MediaConfigure处理函数时,调用g_signal_handler_disconnect是否会阻塞至处理函数执行完毕,从而避免类实例销毁时出现访问已释放内存的非法操作?GObject官方文档对此描述不够明确。


核心结论

g_signal_handler_disconnect不会阻塞等待正在执行的信号处理函数完成,直接按现有代码逻辑执行会存在严重线程安全风险。

具体分析

  • GObject信号系统在触发回调时,不会为正在运行的handler加全局阻塞锁:调用disconnect仅会将该handler从信号的回调列表中移除,后续信号触发不会再调用它,但已经启动执行的handler会继续运行。
  • 如果此时析构函数执行完毕销毁了类实例,正在运行的MediaConfigure中通过pUserData访问类成员的操作,会触发野指针访问/内存非法访问的未定义行为,大概率导致程序崩溃。

线程安全修正方案

方案1:添加互斥锁同步

在类中新增互斥锁成员,确保处理函数执行期间实例不会被销毁:

#include <glib.h>

class CGstreamerStream
{
public:
    void Configure();
    
    ~CGstreamerStream();
    
private:
    static void MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData );
    
    GstRTSPMediaFactory* pFactory = nullptr;
    gulong m_nMediaConfigureHandlerId = 0;
    GMutex m_instanceMutex; // 新增互斥锁
};


void CGstreamerStream::Configure()
{
    m_nMediaConfigureHandlerId = g_signal_connect( pFactory, "media-configure",
        (GCallback)CGstreamerStream::MediaConfigure, this );
}

CGstreamerStream::~CGstreamerStream()
{
    g_mutex_lock(&m_instanceMutex);
    if( m_nMediaConfigureHandlerId )
    {
        g_signal_handler_disconnect( pFactory, m_nMediaConfigureHandlerId );
        m_nMediaConfigureHandlerId = 0;
    }
    g_mutex_unlock(&m_instanceMutex);
}

void CGstreamerStream::MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData )
{
    CGstreamerStream* self = static_cast<CGstreamerStream*>(pUserData);
    g_mutex_lock(&self->m_instanceMutex);
    
    // 在此安全使用类成员
    //...
    
    g_mutex_unlock(&self->m_instanceMutex);
}

方案2:原子标记+延迟销毁

通过原子变量标记实例销毁状态,配合条件变量等待处理函数完成:

#include <atomic>
#include <condition_variable>
#include <mutex>

class CGstreamerStream
{
public:
    void Configure();
    
    ~CGstreamerStream();
    
private:
    static void MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData );
    
    GstRTSPMediaFactory* pFactory = nullptr;
    gulong m_nMediaConfigureHandlerId = 0;
    std::atomic<bool> m_isDestroying = false;
    std::mutex m_mutex;
    std::condition_variable m_cv;
    int m_runningHandlers = 0;
};


void CGstreamerStream::Configure()
{
    m_nMediaConfigureHandlerId = g_signal_connect( pFactory, "media-configure",
        (GCallback)CGstreamerStream::MediaConfigure, this );
}

CGstreamerStream::~CGstreamerStream()
{
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_isDestroying = true;
    }
    
    if( m_nMediaConfigureHandlerId )
    {
        g_signal_handler_disconnect( pFactory, m_nMediaConfigureHandlerId );
        m_nMediaConfigureHandlerId = 0;
    }
    
    // 等待所有正在执行的handler完成
    std::unique_lock<std::mutex> lock(m_mutex);
    m_cv.wait(lock, [this](){ return m_runningHandlers == 0; });
}

void CGstreamerStream::MediaConfigure( GstRTSPMediaFactory *pFactory, GstRTSPMedia *pMedia, gpointer pUserData )
{
    CGstreamerStream* self = static_cast<CGstreamerStream*>(pUserData);
    
    {
        std::lock_guard<std::mutex> lock(self->m_mutex);
        if (self->m_isDestroying)
            return;
        self->m_runningHandlers++;
    }
    
    // 在此安全使用类成员
    //...
    
    {
        std::lock_guard<std::mutex> lock(self->m_mutex);
        self->m_runningHandlers--;
    }
    self->m_cv.notify_one();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 22:05:01