你如何使用POSIX线程声明递归互斥锁?

56

我有点困惑如何使用pthread声明递归互斥锁。 我的目标是让一次只有一个线程能够运行一段代码(包括函数),但经过怀疑后,我发现使用互斥锁不起作用,而应该使用递归互斥锁。以下是我的代码:

pthread_mutex_lock(&mutex);                   // LOCK

item = queue_peek(queue);                     // get last item in queue
item_buff=item;                               // save item to a buffer
queue_removelast(queue);                      // remove last item from queue

pthread_mutex_unlock(&mutex);                 // UNLOCK

所以我尝试做的就是按顺序从队列中读取/删除。

问题在于,没有任何关于如何声明递归互斥锁的示例。或者可能有一些示例,但它们对我来说无法编译。


5
不需要使用递归互斥锁来解决这个问题。只要在访问“queue”的所有线程中使用相同的“mutex”,你提供的示例就可以正常工作。因此,通常将互斥锁包含在队列本身中:“pthread_mutex_lock(&mutex->queue);”,或者如果队列是一个不透明的数据结构,“queue_lock(queue);”(其中“queue_lock()”在内部锁定互斥锁)。 - caf
7
pthread中的一位专家David Butenhof在http://www.zaval.org/resources/library/butenhof1.html上对递归互斥锁进行了幽默的抨击。因此,递归互斥锁通常表明设计有缺陷。 - janneb
1
@janneb:递归互斥锁有很多正确的使用方法,但是对于初学者来说,我强烈建议不要使用它们。 - R.. GitHub STOP HELPING ICE
1
@R..:我认为使用递归互斥锁的正确情况并不多,而且这些情况都可以重新设计以避免使用它们。 - Andriy Tylychko
3
@AndyT:我所知道的递归互斥锁的主要重要用途是当您对共享资源进行操作时,需要单独进行原子操作,但也希望允许将多个操作组合在一起作为原子事务。经典示例是使用flockfile的stdio。 - R.. GitHub STOP HELPING ICE
4个回答

94

Michael Foukarakis的代码几乎正确,但他两次初始化了互斥锁,这会导致未定义行为。应该只是:

pthread_mutex_t Mutex;
pthread_mutexattr_t Attr;

pthread_mutexattr_init(&Attr);
pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&Mutex, &Attr);

我实际上在生产环境中使用这段代码,并且我知道它可以在Linux、Solaris、HP-UX、AIX、Mac OSX和FreeBSD上正确工作。

您还需要添加适当的链接器标志来编译这个程序:

AIX, Linux, FreeBSD:
CPLATFORM += -pthread

mingw32:
LDFLAGS += -lpthread

有些系统需要添加 #define _GNU_SOURCE (否则编译器会抛出 implicit declaration 错误)。 - Mitrakov Artem
我获取到了以下错误信息: main.c:4:24: 错误:在‘&’令牌之前,期望声明说明符或‘...’ pthread_mutexattr_init(&Attr); - Shawn Anderson
3
必须在最后加上pthread_mutexattr_destroy(&Attr),否则将会泄漏内存。 - PoVa

20

要创建一个递归互斥锁,你可以使用以下方法之一:

#include <pthread.h>
/* Don't forget to check the return value! */
int pthread_mutexattr_settype(pthread_mutexattr_t *attr,
                               int type);

类型为 PTHREAD_MUTEX_RECURSIVE 或者是一个初始化器。

例如:

/* ..or PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */
pthread_mutex_t       mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutexattr_t   mta;

或者,在运行时进行初始化(不要同时做两件事,这是未定义的行为):

pthread_mutexattr_init(&mta);
/* or PTHREAD_MUTEX_RECURSIVE_NP */
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE);

pthread_mutex_init(&mutex, &mta);

1
我得到了之前尝试中遇到的错误。无法编译的行是最后三行,我使用带有-pthread标志的GCC。pthread.h也已经被包含进来了。 - Pithikos
1
哪个错误?你从未提到过一个。无论如何,请检查更新后的答案。 - Michael Foukarakis
1
是的,到底是哪个错误,以及使用哪个操作系统?Solaris需要[g]cc -mt来支持多线程应用程序。一些“pthreads”的实现并不完全符合POSIX标准。 - jim mcnamara

18

在Linux环境下(但这不适用于其他系统),如果互斥锁是全局或静态变量,您可以像这样初始化:

static pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

(顺便提一下,这个例子来自于pthread_mutex_init(3)的man文档页!)

2
谢谢提供的信息。因此我在MAC上找到了:static pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; - Val

2

创建互斥锁时需要添加互斥锁属性。

调用pthread_mutexattr_init,然后调用pthread_mutexattr_settype并将其设置为PTHREAD_MUTEX_RECURSIVE,最后使用这些属性调用pthread_mutex_init。有关更多信息,请阅读man pthread_mutexattr_init


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