初始化pthread互斥锁。

21

根据文档,mutex可以通过两种方式初始化:

使用init函数:

pthread_mutex_t theMutex;
pthread_mutex_init(&theMutex, NULL);

使用初始化宏:

pthread_mutex_t result = PTHREAD_MUTEX_INITIALIZER;

关于后者,文档中提到:

在适用默认互斥属性的情况下,宏PTHREAD_MUTEX_INITIALIZER可以用于初始化静态分配的互斥锁。效果应等同于通过调用pthread_mutex_init()函数并将参数attr指定为NULL进行动态初始化,但不执行任何错误检查。

这是否意味着它只能用于静态变量而不能用于局部变量?

C++ 特定

我想要使用以下“工厂函数”:

static pthread_mutex_t GetFastNativeMutex()
{
    static pthread_mutex_t result = PTHREAD_MUTEX_INITIALIZER;
    return result;
}

因为这会使我能够在C++初始化列表中初始化互斥锁,如下所示:

MyClass() : myMutex(GetFastNativeMutex()) {}

这是有效的吗?(顺便说一下,在实践中它起作用。Valgrind也没有投诉。)

更新

如果我正确理解了文档,那么这应该没问题:

#include <pthread.h>

static pthread_mutex_t m0 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;

然而,当查看预处理器输出时(使用gcc -E main.cpp),我看到了以下内容:

static pthread_mutex_t m0 = {0x32AAABA7, {0}};
static pthread_mutex_t m1 = {0x32AAABA7, {0}};
static pthread_mutex_t m2 = {0x32AAABA7, {0}};

事实证明创建了三次同样的互斥锁。我在这里做错了什么?


1
切换到 Boost.Thread 互斥锁,它们在 POSIX 平台上仅包装 pthreads,怎么样?你可以免费获得可移植性,并且它们将成为 C++0x 中的标准。 - Yakov Galka
@ybungalobill 我熟悉 Boost.Thread 库,但对于这个特定的项目,我没有选择。 - StackedCrooked
1
可能不会,如果它能工作,那么它可能不会做你所期望的。请记住,这是一个C接口。 - Martin York
5
“同一个互斥量被创建”是什么意思?有三个互斥量被初始化为相同的值,这就是初始化宏的作用。问题出在哪里? - Jens Gustedt
5个回答

26

关于"原来我创建了三次相同的互斥量,我做错了什么?"

在这里你并没有做错什么。相同的互斥量并没有被创建三次。看起来你把0x32AAABA7解释为一个地址,但实际上它本质上是一个枚举值,带有汉明码保护以使其安全和复杂化。你这里有三个不同的互斥量。


9
标准不允许通过值复制互斥量或通过值返回它们。请参见http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html,其中明确提到了这一点,指出“IEEE Std 1003.1-2001未为此类型[pthread_mutex_t]定义赋值或相等性”。 pthread_mutex的最近亲戚WIN32 CriticalSection也绝对不能通过复制和返回值来实现。因此,您的API将非常不可移植。
即使您可以测试并且它可以工作,我仍建议不要通过值复制(返回)互斥量。
请注意,初始化互斥量与复制已初始化的互斥量是不同的。

在查找了一些资料后,我发现关于pthread_mutex_t的规范中有这样的描述:“所有类型都应定义为适当长度的算术类型,以下是例外情况”(其中它是一个例外情况),以及“以下类型没有定义比较或赋值运算符”。鉴于此,我相信复制互斥锁并不保证有意义。 - Luc Danton
@Luc 谢谢,我在 posix 标准本身(opengroup.org)中找到了相关的文本。 - Andrei

4

仅仅因为你能做到,不代表你应该这么做。

文档明确指出在运行时使用 pthread_mutex_init 和其他宏用于静态分配的互斥锁,我看不出任何不这样做的理由。


@David 错误与否不论,这种“破解”解决方案为了挤出一些循环而导致了后续的错误。重点在于文档明确指出应该使用PTHREAD_MUTEX_INITIALIZER来静态分配互斥锁,并且在运行时获取互斥锁应该使用pthread_mutex_init。无论这个“破解”实际上执行得对与错都是无关紧要的。 - Khaled Nassar
哦,为了澄清一些事情,这个答案是在问题更新之前做出的,它是对上面“更新”行上方问题的回答。 - Khaled Nassar
啊,我看了一下Update行下面的内容。在更新行上方,只有一个互斥锁。如果这是作者想要的,那么,是的,它可以工作。另一方面,如果作者想要每次调用GetFastNativeMutex()都有一个唯一的互斥锁,那么这段代码将无法工作。此外,这是过早的优化。初始化互斥锁的成本非常非常小。 - David Hammen

1

你的GetFastNativeMutex函数并不是一个工厂函数。它总是返回同一个互斥量的副本,工厂的目的是抽象出分配和创建新对象的过程,而不是一直重复使用同一个对象。因此,除非将该互斥量作为全局变量使用,否则它对于任何用途都无效。

引用自这里

在调用pthread_mutex_lock()、pthread_mutex_trylock()、pthread_mutex_unlock()和pthread_mutex_destroy()时引用互斥量的副本的结果是未定义的。


不,它们不同,具有相等的值的互斥锁也不同。 - alternative
@mathepic:在哪里写明复制互斥量会创建一个独立的互斥量?我并不相信复制互斥量会创建一个新的互斥量,事实上我相当确定在Windows中这是不正确的。你能指向相关的文档吗? - littleadv
@mathepic - 实际上,在另一条评论中提供了一个链接,显示你是错误的:“在调用pthread_mutex_lock()、pthread_mutex_trylock()、pthread_mutex_unlock()和pthread_mutex_destroy()时引用互斥锁的副本的结果是未定义的。”- 在这里:http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html。请取消点赞。 - littleadv
我指的是C语言中的复制语义,也就是你会得到位于不同位置的不同数据。另外,提醒一下,我没有给你点踩。实际上,没有人这么做过。 - alternative

-1

如果所有的myMutex实例都应该共享同一个POSIX互斥锁,则它是“有效”的。您目前只有一个互斥锁,而不是每个MyClass对象一个。这绝对不是您想要的,不是吗?


1
该函数返回互斥锁的副本。 - StackedCrooked
你很幸运:在Linux上是正确的(我在源代码中查找了)。当你复制一个pthread_mutex_t时,你会得到一个具有相同状态的“克隆”。然而,这种行为可能不可移植。在所有POSIX平台上都不是这种情况。想象一下一个实现,其中pthread_mutex_t只包含一种互斥锁ID类型的句柄。仅仅作为一个例子,在Linux上使用pthread_t将无法工作。 - dmeister

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