我需要编写加载共享库的类. 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()之前触摸锁:
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静态对象都被销毁之前不会被销毁.我不知道任何平台不是这种情况.