2

我正在查看一段代码,它直到最近才起作用。基本上,我有一个 C++ 类,在其中我使用 G_LOCK_DEFINE 宏保护变量。

class CSomeClass {
private:
    gulong mSomeCounter;
    G_LOCK_DEFINE(mSomeCounter);

public:
    CSomeClass ();
}

构造函数在单独的 .cpp 文件中实现。

CSomeClass::CSomeClass()
{
    G_LOCK(mSomeCounter);
    mSomeCounter = 0;
    G_UNLOCK(mSomeCounter);
}

This variable is accessed in several functions, but the principle is always the same. Now, as already said, the code compiles fine and in fact did also run flawlessly in the past. Now, since recently, I'm getting a deadlock, whenever I come across a G_LOCK command. For debugging, I already restricted the program to just one thread, to exclude logical errors.

I did update to Ubuntu 16.04 beta recently, which pushed my glib version to 2.48.0-1ubuntu4. I already checked the changelog for relevant information on G_LOCK, but couldn't find anything. Did anybody else notice funny effects, when using G_LOCK macros with the recent glib version? Did I miss some changes here?

4

1 回答 1

4

首先,G_LOCK_DEFINE所做的只是创建一个变量,其名称将其保护的变量的GMutex名称编码为. 因此,我们可以将您的代码扩展为:G_LOCK_DEFINE(mSomeCounter)GMutex g__mSomeCounter_lock;

class CSomeClass {
    private:
        gulong mSomeCounter;
        GMutex g__mSomeCounter_lock;

    public:
        CSomeClass ();
};

CSomeClass::CSomeClass()
{
    g_mutex_lock(&g__mSomeCounter_lock);
    mSomeCounter = 0;
    g_mutex_unlock(&g__mSomeCounter_lock);
}

这里的根本问题是您没有初始化class 的任何成员CSomeClass您将在构造函数中为其中的一些赋值,但绝对不会初始化它们。大括号中的赋值和使用初始化器之间存在差异,例如:

    CSomeClass::CSomeClass() : mSomeCounter(0)

因此,创建的、针对变量命名的互斥锁可能包含垃圾。glib 代码中可能没有任何更改会导致这种情况发生,更有可能是对其他库的更改改变了您的应用程序的内存布局,从而发现了错误。

glib 文档提示您需要g_mutex_init互斥锁:

已分配在堆栈上或作为更大结构的一部分

您不需要g_mutex_init互斥:

不必初始化已静态分配的互斥锁

类实例几乎总是不是静态分配的。

您需要修复构造函数以确保它“正确”初始化互斥锁,例如:

CSomeClass::CSomeClass()
{
    g_mutex_init(&G_LOCK_NAME(mSomeCounter));
    G_LOCK(mSomeCounter);
    mSomeCounter = 0;
    G_UNLOCK(mSomeCounter);
}

TBH,我会将互斥锁放入类持有者中,并将其作为其中的一部分进行初始化,而不是按照您的方式进行初始化,以确保它作为标准 C++ RAII 语义的一部分被初始化、锁定和解锁。

如果您使用小型主存根,例如:

main() {
    { CSomeClass class1; }
    { CSomeClass class2; }
    { CSomeClass class3; }
}

和你的代码,无论如何它很有可能会挂起。(我的 mac 使示例崩溃:GLib (gthread-posix.c): Unexpected error from C library during 'pthread_mutex_lock': Invalid argument. Aborting..

一些简单的,例如,非生产包装器来帮助 RAII:

class CGMutex {
    GMutex    mutex;

    public:
    CGMutex() {
        g_mutex_init(&mutex);
    }

    ~CGMutex() {
        g_mutex_clear(&mutex);
    }

    GMutex *operator&() {
        return &mutex;
    }
};

class CGMutexLocker {
    CGMutex &mRef;
    public:
    CGMutexLocker(CGMutex &mutex) : mRef(mutex) {
        g_mutex_lock(&mRef);
    }
    ~CGMutexLocker() {
        g_mutex_unlock(&mRef);
    }
};

class CSomeClass {
    private:
        gulong mSomeCounter;
        CGMutex mSomeCounterLock;

    public:
        CSomeClass ();
};

CSomeClass::CSomeClass()
{
    CGMutexLocker locker(mSomeCounterLock); // lock the mutex using the locker
    mSomeCounter = 0;
}

mSomeCounter 初始化确保计数器被初始化,否则它将产生垃圾。

于 2016-04-18T13:24:53.030 回答