我需要编写加载共享库的类。 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;
这个代码的问题是它可能会崩溃,因为它依赖于程序终止时被破坏的静态顺序。 如果locking在myLib之前就会崩溃。
这怎么能够以一种安全的方式来编写,并且不依赖于静态破坏的顺序?
不幸的是,我认为避免这种情况的唯一方法是使用不可移植的一次性初始化指令,并且避免破坏锁。 有两个基本的问题需要处理:
这些约束的组合迫使您使用不可移植的机制来创建锁。
在pthread上,处理这个问题最直接的方法是使用PTHREAD_MUTEX_INITIALIZER
,它可以让你静态地初始化锁:
class LibLoader{ static pthread_mutex_t mutex; // ... }; // never destroyed pthread_mutex_t LibLoader::mutex = PTHREAD_MUTEX_INITIALIZER;
在Windows上,您可以使用同步一次性初始化 。
或者,如果你能保证在主运行之前只有一个线程,你可以使用单例模式而不会被破坏,而只需要在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类型的静态对象不会被销毁。 我不知道有什么平台在这种情况下。
总结需求:需要多个LibLoader
实例,每个LibLoader
实例用于不同的库,但必须存在单个锁以确保它们不会覆盖彼此的错误代码。
一种方法是依靠文件内的静态初始化和销毁顺序。
更好的方法是不要在NeedsALib
(等)中创建LibLoader
静态字段。 看来这些客户端类可以在构造函数中传递正确的LibLoader
实例。
如果在其客户类之外创建LibLoader
实例不方便,可以创建所有静态字段(锁和加载器)指针,并使用单例模式进行惰性初始化。 然后,当你创建第一个装载器时,它最终也会创建锁。 单身人士本身需要锁定在这里,但你也许可以在产生你的线程之前运行它。 破坏也是明确的,在你的控制之下。 你也可以只使用加载器(保持静态锁定)。
另外,如果LibLoader
没有很多的状态来存储,你可以让每个客户类( NeedsALib
等)实例化它自己的LibLoader
。 虽然这确实是很浪费的。