我需要编写加载共享库的类。 dlopen()/dlerror() 序列需要一个锁以确保线程安全。
class LibLoader {
public:
LibLoader(string whichLib);
bool Load() { Wait(lock); ... dlopen() ... dlerror() ... }
bool Unload() { Wait(lock); ... dlclose() ... dlerror() ... }
bool IsLoaded() {...}
// ... access to symbols...
private:
static Lock lock;
}
Lock Lock::lock;
这个类的用户(同时会有多个)会想要让它成为这个类的静态成员,以避免为类的每个对象多次加载共享库:
class NeedsALib {
public:
NeedsALib() { if (!myLib.IsLoaded()) { myLib.Load(); } }
private:
static LibLoader myLib;
}
LibLoader::myLib;
这段代码的问题是它可能/将会崩溃,因为它依赖于程序终止时静态被破坏的顺序。如果锁在 myLib 之前消失,它将崩溃....
如何以线程安全且不依赖于静态销毁顺序的安全方式编写?
最佳答案
不幸的是,我认为避免这种情况的唯一方法是既使用不可移植的一次性初始化指令,又完全避免破坏锁。您需要处理两个基本问题:
- 如果两个线程第一次争用锁会怎样? [即,您不能延迟创建锁]
- 如果过早销毁锁会怎样? [即,您不能静态创建锁]
这些约束的结合迫使您使用不可移植的机制来创建锁。
在 pthreads 上,最直接的处理方法是使用 PTHREAD_MUTEX_INITIALIZER
,它允许您静态初始化锁:
class LibLoader{
static pthread_mutex_t mutex;
// ...
};
// never destroyed
pthread_mutex_t LibLoader::mutex = PTHREAD_MUTEX_INITIALIZER;
在 Windows 上,您可以使用 synchronous one-time initialization .
或者,如果你能保证在main运行之前只有一个线程,你可以使用无破坏的单例模式,并强制在main()之前触及锁:
class LibLoader {
class init_helper {
init_helper() { LibLoader::getLock(); }
};
static init_helper _ih;
static Lock *_theLock;
static Lock *getLock() {
if (!_theLock)
_theLock = new Lock();
return _theLock;
}
// ...
};
static init_helper LibLoader::_ih;
static Lock *LibLoader::_theLock;
请注意,这使得 POD 类型的静态对象在所有非 POD 静态对象都被销毁之前不会被销毁的假设可能不可移植(但很可能是真的)。我不知道有哪个平台不是这种情况。
关于c++ - 编写一个可以用作静态但需要锁的 C++ 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8276272/