我有一个使用 dlopen 由程序 (P) 动态加载的库 (L)。L 实现了一个插件接口,因此回调它的父级以获得一些功能。
P 内部是一个单例对象,它动态创建线程池对象 A。我需要从 L 访问 A。
但是,因为单例通过使用静态变量来工作,所以当 L 被加载时,它最终会创建它自己的实例,这在某些情况下会很好,但我想要在 P 中创建的实例。有没有办法解决这个问题?
声明的文件范围名称static
具有内部链接。内部链接意味着它们对其他翻译单元不可见,即使在没有任何动态库的“经典”链接模型中也是如此。鉴于静态甚至对同一可执行文件中的其他翻译单元不可见,因此期望它们从附加的动态库中可见是不合理的。
您必须想办法使用外部动态符号来实现必要的链接。也许单例根本不能有一个内部名称,但必须有一个外部名称。
L 创建自己的对象实例不仅因为对象是静态的,还因为您已将定义该单例和线程池函数的线程池模块链接到 L 中。即使对象具有外部名称,也可能发生这种情况,具体取决于库的链接方式。
您必须选择线程池服务将驻留的单个对象,然后确保它仅驻留在那里。你的项目没有一个实用程序库,你可以在其中坚持这种事情吗?
您可以坚持提供线程池 API 的程序可执行文件 P 的模型。这真的是一回事。程序 P 是另一个动态对象,有效地充当线程池模块的库,它提供给自己和其他共享对象。
无论该线程池模块位于何处,请确保您没有将该模块的副本静态链接到其他对象:它仅位于一个位置。
如果线程池单例的外部名称是该 API 的一部分(每个人都知道它的文档名称并直接使用它,将该全局池传递给 API 函数),那么该名称应该是外部的,并在头文件中声明。
如果单例是私有的,那么你必须想办法隐藏它,比如让它隐含在函数调用中(只有一个线程池,就是这样),或者在某种程度上抽象对它的访问(提供一个ensure_thread_pool
) 函数,如果线程池不存在则创建一个线程池,否则以线程安全的方式返回先前创建的线程池。
想一想:为什么不,例如,stdin
有stdout
这个问题?为什么不是每个库都实例化自己的stdout
流并在该流上调用自己的fprintf
函数?为什么,很明显,因为这些东西都存在于一个地方:C 库。他们的副本不在其他地方;其他地方只是通过动态符号引用它们。
L 中不应该有static
A。让 P 将 A 的地址传递给 L,即L.init(&A)
。