1

我实际上面临一个巨大的问题:我有一个单例课程。我的程序是一个多线程的MFC,所以可以从不同的线程调用单例的构造函数。我用互斥锁包围了单例实例获取函数,以避免死锁和多重构造。下面的意思是函数的调用在第一次直接失败。

该函数看起来像(LOG_MSG 宏将给定的字符串记录到我的日志文件中):

MyClass& MyClass::singleton ()
{
    LOG_MSG("MyClass::singleton -> jump in");

    static CMutex oInitMutex;
    try
    {
        CSingleLock oSingleLock((CSyncObject *) &oInitMutex, TRUE);
        LOG_MSG("!!! SINGLETON LOCK OKAY !!!");
        static MyClass oMyClassInstance;
        LOG_MSG("!!! SINGLETON CONSTRUCTION OKAY !!!");
        return oMyClassInstance;
    }
    catch(...)
    {
        CString excMsg("Unexpected exception by creating MyClass singleton instance!");
        LOG_MSG(excMsg);
        throw excMsg;
    }
}

我发现,单例对象的构造不会失败(因为我们得到“!!!单例构造OKAY !!!”消息)。

日志输出说:

09.04.2013 ;07:14:51:832;"MyClass::singleton -> jump in"
09.04.2013 ;07:14:51:841;"!!! SINGLETON LOCK OKAY !!!"
... (constructor logs => NOTHING unexpected in it!!! everything runs fine, as they must!!!)
09.04.2013 ;07:14:52:125;"!!! SINGLETON CONSTRUCTION OKAY !!!"
09.04.2013 ;07:14:52:170;"Unexpected exception by creating MyClass singleton instance!"

这是什么意思?return语句什么时候会抛出异常???请帮我解决这个问题...

4

2 回答 2

6

虽然这不能回答您的特定问题,但它仍然是您整体问题的解决方案:您根本不需要互斥锁C++11 标准 [stmt.dcl]§4 指定(在讨论static函数的局部变量时):

如果在变量初始化时控制同时进入声明,则并发执行将等待初始化完成。88如果在初始化变量时控制以递归方式重新进入声明,则行为未定义。

注释 88 是:

注 88:实现不能在初始化程序的执行中引入任何死锁。

换句话说,编译器为你引入了同步;无需手动执行此操作。

于 2013-04-09T06:49:18.350 回答
1

解决方案:

我发现了问题,这并不容易。从我的错误中吸取教训,我将分享代码,导致失败。

在构造函数中,我以不正确的方式使用sscanf函数调用:

const char* sBuffer;
// some stuff here that fills the sBuffer up
sscanf(sBuffer, "%X %X %X %X", &tags_[0], &tags_[1], &tags_[2], &tags_[3]);

该数组在单例类中定义为:

private:
    char tags_[4];

当心:格式代码等待特定类型的变量才能写入数据。例如:在我的情况下,tags_数组应该是整数数组而不是字符数组。由于单例类应该存储tags_,在索引第二个、第三个和第四个元素后,sscanf函数将写入一个未定义的内存位置,静态类的数据将被覆盖,这会导致它的多次实例化,但是:这可以可以通过互斥来避免,但在这种情况下,互斥对象将被覆盖。有关 (s)scanf 所需类型的更多信息,请参见此处此处

结论:

要聪明并注意使用 C 函数/低级调用。非常注意输入参数,不仅是它们的值,还有它们的类型。如果您按预期支持其他类型,您的程序的行为将是未定义的,但您不会得到编译器错误:以后很难发现这个问题。

于 2013-04-10T07:10:42.550 回答