G_LOCK行为从glib 2.46更改为glib 2.48?

时间:2016-04-18 08:01:45

标签: c++ multithreading glib ubuntu-16.04

我正在查看一段代码,直到最近才开始工作。基本上,我有一个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);
}

在几个函数中访问此变量,但原则始终相同。现在,正如已经说过的那样,代码编译得很好,实际上过去也运行得很完美。现在,从最近开始,每当遇到G_LOCK命令时,我都会遇到死锁。为了进行调试,我已经将程序限制在一个线程中,以排除逻辑错误。

我最近更新了Ubuntu 16.04 beta,将我的glib版本推到了2.48.0-1ubuntu4。我已经在更改日志中查看了有关G_LOCK的相关信息,但找不到任何内容。当G_LOCK宏与最近的glib版本一起使用时,有没有其他人注意到有趣的效果?我在这里想念一些变化吗?

1 个答案:

答案 0 :(得分: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);
}

这里的根本问题是您没有初始化类CSomeClass任何成员。您将在构造函数中为的某些赋值,但您肯定不会初始化它们。大括号中的赋值与使用初始值设定项之间存在差异,例如:

    CSomeClass::CSomeClass() : mSomeCounter(0)

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

g_mutex_init隐私所需的glib documentation提示:

  

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

您不需要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初始化确保计数器被初始化,否则它将有垃圾。