多线程程序中栈锁的正确清理及线程重启相关技术疑问
多线程栈锁相关问题与解答
问题背景
我正在开发一款多线程程序,其中一个模块的栈锁代码如下:
/*! Helper class. Cannonical stack lock */ class StackLock { public: StackLock() { pthread_mutex_lock(&lock_); } ~StackLock() { pthread_mutex_unlock(&lock_); } static void Init(); private: static pthread_mutex_t lock_; }; pthread_mutex_t StackLock::lock_; void StackLock::Init() { pthread_mutexattr_t mat; pthread_mutexattr_init(&mat); //The type of lock set is recursive pthread_mutexattr_settype(&mat, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&lock_, &mat); } /*! Initialization. Called when the thread is restarted. * @returns 0; */ int32_t LibInit() { // .. other code StackLock::Init(); return 0; } // There is no "cleanup" function.
该栈锁在模块中的使用方式如下:
OtherObject* OtherClass::class_method() { OtherObject *object(nullptr); if (size() != 0) { StackLock s; // ... // There is no StackLock destructor } return object; }
我们的架构支持线程在运行时重启。我注意到pthread库允许使用pthread_mutex_destroy销毁互斥锁,但当前代码中并无相关清理方法。现提出以下技术疑问:
- 当StackLock对象离开
OtherClass::class_method()的作用域时,是否会自动析构? - 由于未调用
pthread_mutex_destroy或pthread_mutexattr_destroy,多次重启该模块是否会存在内存泄漏风险? - 安全销毁栈锁并清理模块的正确方式是什么?
解答
问题1:StackLock对象离开作用域时是否自动析构
会自动析构。代码里StackLock类已经明确定义了析构函数~StackLock(),当局部对象s离开if代码块的作用域时,C++会自动调用它的析构函数,执行pthread_mutex_unlock(&lock_)完成解锁。你看到的注释// There is no StackLock destructor是错误的,实际代码里析构函数是存在的。
问题2:未调用销毁函数是否会导致内存泄漏
会存在内存泄漏和未定义行为的风险,分两种情况:
pthread_mutexattr_t mat:在Init()函数里,pthread_mutexattr_init会为这个属性对象分配内部资源,但代码里没有调用pthread_mutexattr_destroy释放,每次调用Init()都会泄漏这部分资源。- 静态互斥锁
lock_:每次线程重启调用LibInit()时,都会重新调用pthread_mutex_init初始化已经存在的互斥锁对象——这本身就是未定义行为,而且如果之前的互斥锁没有被销毁,会导致系统线程库的内部锁资源无法释放,多次重启后可能耗尽资源甚至触发程序崩溃。
问题3:安全销毁栈锁并清理模块的正确方式
- 添加静态清理函数:给
StackLock类添加静态的Cleanup函数,负责销毁互斥锁:
void StackLock::Cleanup() { pthread_mutex_destroy(&lock_); }
- 修复属性对象泄漏:在
Init()函数里,pthread_mutex_init完成后立即销毁属性对象,因为属性仅在初始化锁时需要:
void StackLock::Init() { pthread_mutexattr_t mat; pthread_mutexattr_init(&mat); pthread_mutexattr_settype(&mat, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&lock_, &mat); // 用完属性立即销毁,避免泄漏 pthread_mutexattr_destroy(&mat); }
- 规范模块重启流程:重启线程前,必须先调用
StackLock::Cleanup()销毁已初始化的互斥锁,再调用LibInit()重新初始化。注意调用Cleanup()时,要确保没有任何线程还持有这个锁——可以在模块停止阶段,先停止所有业务线程,等待所有锁都被释放后再执行清理。 - 修正错误注释:删掉
OtherClass::class_method()里的// There is no StackLock destructor注释,避免误导后续维护者。
内容的提问来源于stack exchange,提问作者jms
相关产品推荐
相关产品推荐

