c++ - 如何在 DLL 的成员函数中使用互斥锁/临界区
问题描述
我正在创建一个驻留在 DLL 中的 COM 类(带有 ATL)。对于我的成员函数之一,根据某些条件,我可能会使用必须初始化和终止的第三方库(Adobe 的 XMP SDK)。所以基本上,我将有一个看起来像这样的成员函数:
void CMyClass::MyMemberFunction()
{
SXMPMeta::Initialize();
// ...
SXMPMeta::Terminate();
}
现在根据 Adobe XMP 库文档,
您必须以单线程方式调用初始化和终止函数。. . .
同时,我相信文件资源管理器可能会在不同的线程上创建我的类的多个实例。因此,如果我理解正确,听起来我会有一个关键部分,我需要在库初始化和终止周围使用互斥锁(但如果有错请纠正我)。
我不清楚该怎么做。我不确定是否应该使用ATL 临界区类之一,或者可能是 CRITICAL_SECTION。CRITICAL_SECTION 似乎是一个不错的选择,但示例显示它在 main() 中初始化。如果您在 DLL 中会发生什么?我不想开始弄乱 DllMain()。任何帮助或想法将不胜感激。
根据 Gem 的建议,我尝试了以下方法:
struct XMPLibraryInitializer
{
XMPLibraryInitializer()
{
// Initialize libraries
if (!SXMPMeta::Initialize() || !SXMPFiles::Initialize())
{
XMP_StringPtr pszErrorMessage = "Libraries failed to load";
throw XMP_Error(kXMPErr_InternalFailure, pszErrorMessage);
}
ATLTRACE("\nXMP library initialized on thread %lu\n", GetCurrentThreadId());
}
~XMPLibraryInitializer()
{
// Terminate XMP libraries
SXMPFiles::Terminate();
SXMPMeta::Terminate();
ATLTRACE("\nXMP library terminated on thread %lu\n", GetCurrentThreadId());
}
};
HRESULT MyFunc()
{
// Statically initialize the Adobe XMP library
try
{
static XMPLibraryInitializer xmpLibraryInitializer;
}
catch (const XMP_Error & e)
{
return E_UNEXPECTED;
}
// ...
}
这似乎运行良好,除了我看到的输出是
XMP 库在线程 5820 上初始化 ... XMP 库在线程 3104 上终止
对不同的线程号有什么解释吗?如果数字不同,这是否意味着它不符合要求单线程初始化和终止的文档?
解决方案
如果您想保留它直到程序退出,一个简单的静态单例函数就足够了。静态初始化保证仅以一种有效的线程安全方式(来自 C++11)完成,通常涉及对保护标志的互斥体双重检查。
在您无法确定是否已设置 ATX 的任何地方,您都会调用它。在一些重复事务的中间调用它没有意义,但也没有坏处。所以一定要在 GiveMeAToken() 中调用它,但不要在 UseToken() 中调用它,因为您知道必须调用它才能获取令牌?
我更新了这个以显示如何保持简单的 bool Initialise 成功状态,尽管 SXMPMeta::Initialize() 的实际返回可能更复杂:
struct SomeATXThing
{
bool good;
SomeATXThing()
{
good = SXMPMeta::Initialize();
}
~SomeATXThing()
{
if (good)
SXMPMeta::Terminate();
}
};
SomeATXThing* AtxEnsureSingleton()
{
static SomeATXThing someATXThing;
return someATXThing.good ? &someATXThing : nullptr;
}
请注意,单例的一个警告是,它们在构造结束时以相反的顺序被破坏。其他一些独立的较早构造的单例在其销毁期间不得依赖此单例的存在,但一个单例构造函数在构造期间调用另一个单例构造函数并在销毁期间再次调用是安全的。除了 ABEND 之外,这条规则的执行并不容易。
您可以在对象所在的位置进行凤凰重新激活(通常依赖于销毁后对象实例地址的未定义行为存在),但它是一个悲伤的负担。
推荐阅读
- python - 在下面的python for 循环中使用 {} [()] 的目的是什么?
- vb.net - 检查分组框内的多个控件类型是否为空并显示空控件的标签
- python - 标记包含字符串的行
- ruby-on-rails - 如何将附件上传到通过电子邮件收到的申请?
- javascript - google.script 用部分文件名在谷歌驱动器中创建文件夹,然后移动文件
- javascript - 如何在 C++ 中从 V8 调用 Javascript 函数
- wso2 - WSO2 AM-Analytics 工作人员错误 java.lang.OutOfMemoryError:超出 GC 开销限制
- ssas-tabular - 未能在 Visual Studio 2019 中部署分析服务表格项目
- python - 复制数据框时出现名称错误
- php - 如何在select语句中分配php变量where条件