C++互斥锁的正确使用方法

3

我有一个多线程项目,我已经使用--tool=helgrind选项通过valgrind运行了它,发现了一些错误。我在那里使用的互斥锁正是我在网上找到的如何使用,你能告诉我哪里出了问题吗?

#include <iostream>
#include <pthread.h>

#define MAX_THREADS     100
#define MAX_SESSIONS    100

static pthread_mutex_t  M_CREATE_SESSION_LOCK= PTHREAD_MUTEX_INITIALIZER;


.....



void connection::proccess(threadVarsType &THREAD) {
    ....

    pthread_mutex_lock(&M_CREATE_SESSION_LOCK);

    unsigned int ii;
    for (ii=0; ii<MAX_SESSIONS; ii++) {
        if (SESSION[ii]==NULL) {
            break;
        }
    }
    if (ii==MAX_SESSIONS-1) {
        ....
        pthread_mutex_unlock(&M_CREATE_SESSION_LOCK);                       // unlock session mutex
        ....
        return;
    } else {
        ....
        pthread_mutex_unlock(&M_CREATE_SESSION_LOCK);                       // unlock session mutex
        ....
    }

    ....
}

以及错误信息:

==4985== Thread #1's call to pthread_mutex_lock failed
==4985==    with error code 22 (EINVAL: Invalid argument)
    ....
==4985== Thread #1 unlocked an invalid lock at 0x4E7B40
==4985==    at 0x32CD8: pthread_mutex_unlock (hg_intercepts.c:610)
    ....
==4985== Thread #1's call to pthread_mutex_unlock failed
==4985==    with error code 22 (EINVAL: Invalid argument)
    ....
==4985== Thread #1's call to pthread_mutex_lock failed
==4985==    with error code 22 (EINVAL: Invalid argument)
    ....
==4985== Thread #1 unlocked an invalid lock at 0x4E7B40
==4985==    at 0x32CD8: pthread_mutex_unlock (hg_intercepts.c:610)
    ....
==4985== Thread #1's call to pthread_mutex_unlock failed
==4985==    with error code 22 (EINVAL: Invalid argument)

1
考虑使用Boost.Thread(http://www.boost.org/doc/libs/release/doc/html/thread.html),它在底层使用pthread。与原始的pthread API相比,它更容易使用。 - Emile Cormier
我正在寻找简单的解决方案,因为如果我添加了数百万个库,那么程序将变得1000倍慢且100倍更大 - 而我将一无所获 :-( - tominko
pthread 是一个 C 接口。它没有异常;因此在调用这些函数时,你必须检查错误代码。 - Martin York
1
@tominko:如果您链接到库的静态版本,使用Boost.Thread不会使您的程序变得更大。它也不会明显变慢。如果您的程序最终变大了几KB,那又有什么关系呢?软件已经不再分发在1.44MB的软盘上了,您知道的。很抱歉,但是您对于使用Boost等库存在一些严重的误解。 - Emile Cormier
但是我的程序将在每秒运行数千个线程(我希望如此;-)),在这种情况下,我必须计算每个kb - 例如,我使用自己的字符串类仅具有我需要的功能和优化只为我的需求,它大约快10倍,并且与std :: string相比使用约一半的内存....再次强调-不要过多地使用库也将对我的学习有很大帮助。 - tominko
显示剩余2条评论
4个回答

4

首先,始终检查您的函数调用的返回值。如果pthread调用失败,最好选择仅调用abort(),如果启用了该选项,则会核心转储,或者如果使用调试器运行,则会降入调试器。

pthread函数调用实际上不应该失败,这意味着您的程序出现了严重问题。在C或C++程序中,通常会导致神秘故障的是内存损坏。 使用valgrind以其正常模式检查它。

可能导致pthread调用失败的另一件事是不使用-pthread进行编译。如果使用GCC,您应该使用类似于gcc -pthread的命令进行编译和链接。这将链接pthread库,并设置一些可能对系统标头文件很重要的预处理器定义。

有些系统会成功编译和链接使用pthread调用的程序,而不将其链接到pthread库。这样可以使程序或库变得线程安全,而不必实际使用线程。除非链接了真正的pthread库,否则线程调用将链接到虚拟函数,这可能导致某些函数调用失败。

因此,请确保您正在使用正确的编译器选项构建以包含pthread库。

另一个可能的原因是,如果您正在构建某种混合操作系统,例如它最初是Linux 2.4,然后在某个时候升级到Linux 2.6 NPTL(我曾经处理过这样的问题)。如果您尝试使用具有过时的PTHREAD_MUTEX_INITIALIZER定义或错误大小的pthread_mutex_t类型的旧标头文件进行编译,则可能会导致该问题。


@tominko:您应该编辑您的问题并在其中放置操作系统详细信息。此外,您应该使用pastebin或codepad发布完整的可编译代码示例。 - Zan Lynx
@tominko:如果你确实在使用FreeBSD,请仔细检查你是否只使用系统提供的库,不要使用/usr/local中的任何内容,也不要使用从端口自行编译的任何内容。我已经无数次尝试在FreeBSD上升级某些东西,最终放弃并重新安装新版本。我讨厌FreeBSD他们愚蠢的软件包系统 - Zan Lynx
哦,关于FreeBSD的厌恶,我运行Gentoo已经好几年了,非常喜欢它。只是FreeBSD的系统非常糟糕,并且三个不同的FreeBSD用户会推荐三种不同的错误方式来解决问题。 - Zan Lynx
FreeBSD很好 - 它看起来像一些奇怪的OSX“特性”;-) ...问题就解决了...但如果您有一些关于test_and_set C++函数的经验,那仍然是有趣的。 - tominko
是的,但我发现在许多开源库和实用程序中,你会遇到这样的奇怪问题... 要么需要修改一些东西才能编译,要么你会得到意外的行为,而没有人能够真正诊断它,因为他们没有在他们的Linux机器上经历过。至于开发,到目前为止,我真的很喜欢使用Kate和gnu工具链。比起搞Xcode简单多了 :) - Jason
显示剩余16条评论

2

该错误提示互斥锁的初始化存在问题。很难确定具体原因,但请确保在正确的地方进行初始化。


它位于main.cpp的开头,因为它需要对所有线程可见,并且connection::process函数由线程调用。 - tominko
2
你能发布整个代码吗?看起来问题不在你到目前为止发布的代码中。 - Null Set
这是测试代码:http://codepad.org/LFbgYVbH 但问题只出现在OSX上。 - tominko

2
Helgrind文档页面上,他们提到可能会有一些应该被抑制的误报...你可能因为遇到了这些问题,尽管表面上看起来你并没有错误地使用pthread互斥量。

以下是他们写的内容:

Helgrind的错误检查在系统线程库(libpthread.so)内部无法正常工作,通常会在那里观察到大量(虚假)错误。 Valgrind的抑制系统然后过滤掉这些错误,因此您不应该看到它们。

如果您看到任何与最内层堆栈帧相关联的对象是libpthread.so或ld.so的竞争错误,请在http://www.valgrind.org/上提交错误报告。

他们还指出您应该使用“受支持的Linux发行版”...他们没有具体说明这意味着什么,但如果您使用的是非Linux操作系统,这也可能导致一些“虚假阳性”。向开发团队咨询可能是值得的,看看他们对此有何看法。


我不知道我在这里该怎么做。 - tominko
connection 类中添加一行代码,写入 static pthread_mutex_t M_CREATE_SESSION_LOCK;,然后在 .cpp 文件内修改互斥定义,仅将其设置为 pthread_mutex_t connection::M_CREATE_SESSION_LOCK = PTHREAD_MUTEX_INITIALIZER; ... 或者你需要在类外访问这个互斥量吗? - Jason
不需要从类外部访问...但即使进行了这种修改,结果仍然相同;-(可能是valgrind将某些非错误的东西识别为错误吗? - tominko

1

在调用pthread_mutex_lock时出现错误EINVAL,这意味着两种可能性。

互斥锁是使用协议属性PTHREAD_PRIO_PROTECT创建的,并且调用线程的优先级高于互斥锁的当前优先级上限。

或者

由mutex指定的值不是一个初始化的互斥对象。

第二种情况似乎更有可能。尝试在main函数中使用int error = pthread_mutex_init(&M_CREATE_SESSION_LOCK, NULL);初始化互斥锁并检查是否存在错误,而不是像您目前正在使用的宏一样进行初始化。


@tominko:你可能在调用pthread_mutex_lock之前的某个时刻调用了pthread_mutex_destroy吗? - Null Set
我只有四个操作会涉及到 M_CREATE_SESSION_LOCK - 创建、锁定和两次解锁。 - tominko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接