如何在 DLL 的成员函数中使用 mutex/critical 部分

How to use a mutex/critical section within a member function inside a DLL

我正在创建驻留在 DLL 中的 COM class(使用 ATL)。对于我的成员函数之一,根据某些条件,我想潜在地使用第三方库(Adobe 的 XMP SDK),它必须被初始化和终止。所以基本上,我会有一个看起来像这样的成员函数:

void CMyClass::MyMemberFunction()
{
    SXMPMeta::Initialize();

    // ...

    SXMPMeta::Terminate();
}

现在根据 Adob​​e XMP 库文档,

You must call the initialization and termination functions in a single-threaded manner . . . .

同时,我相信文件资源管理器可能会在不同的线程上创建我的 class 的多个实例。所以如果我理解正确的话,听起来我会有一个关键部分,我需要在库初始化和终止周围使用互斥体(但如果错了请纠正我)。

我不清楚该怎么做。我不确定我是否应该使用其中一个 ATL critical section classes or perhaps CRITICAL_SECTION. CRITICAL_SECTION seemed like a nice option, but the example 显示它正在 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 library initialized on thread 5820 ... XMP library terminated on thread 3104

线程数不同有什么解释吗?如果数字不同,是否意味着它不符合要求单线程初始化和终止的文档?

一个简单的静态单例函数就足够了,如果您想保留它直到程序退出。 静态初始化保证只以一种有效的线程安全方式(来自 C++11)完成一次,通常涉及对保护标志的互斥锁双重检查。

您可以在无法确定是否已经设置好 ATX 的每个地方调用它。在一些重复事务的中间调用它没有意义,但也没有坏处。所以一定要在 GiveMeAToken() 中调用它,但不要在 UseToken() 中调用它,因为您知道必须调用它才能获得令牌?

我更新了这个以展示如何保持简单的 bool 初始化成功状态,尽管来自 SXMPMeta::Initialize() 的实际 return 可能更复杂:

struct SomeATXThing
{
    bool good;
    SomeATXThing()
    {
        good = SXMPMeta::Initialize();
    }
    ~SomeATXThing()
    {
        if (good)
            SXMPMeta::Terminate();
    }
};

SomeATXThing* AtxEnsureSingleton()
{
    static SomeATXThing someATXThing;
    return someATXThing.good ? &someATXThing : nullptr;
}

请注意,单例的一个警告是它们的销毁顺序与构造结束的顺序相反。一些其他早期独立构造的单例在其销毁期间不得依赖于此单例的存在,但一个单例构造函数在构造期间和在销毁期间再次调用另一个构造函数是安全的。这条规则的执行并不容易,除了 ABEND。

可以在对象所在的地方做phoenix reactivation(一般是依赖销毁后对象实例地址的undefined behavior存在),但是很麻烦