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
相关产品推荐
相关产品推荐

