【发布时间】:2019-10-02 01:10:40
【问题描述】:
我从各种渠道获悉 (1, 2) 应该避免使用递归互斥锁,因为这可能是黑客入侵或 糟糕的设计。但是,有时我认为它们可能是必要的。有鉴于此, 以下是递归互斥锁的合适用例吗?
// main.c
// gcc -Wall -Wextra -Wpedantic main.c -pthread
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct synchronized_counter
{
int count;
pthread_mutex_t mutex;
pthread_mutexattr_t mutexattr;
} synchronized_counter;
synchronized_counter* create_synchronized_counter()
{
synchronized_counter* sc_ptr = malloc(sizeof(synchronized_counter));
assert(sc_ptr != NULL);
sc_ptr->count = 0;
assert(pthread_mutexattr_init(&sc_ptr->mutexattr) == 0);
assert(pthread_mutexattr_settype(&sc_ptr->mutexattr,
PTHREAD_MUTEX_RECURSIVE) == 0);
assert(pthread_mutex_init(&sc_ptr->mutex, &sc_ptr->mutexattr) == 0);
return sc_ptr;
}
void synchronized_increment(synchronized_counter* sc_ptr)
{
assert(pthread_mutex_lock(&sc_ptr->mutex) == 0);
sc_ptr->count++;
assert(pthread_mutex_unlock(&sc_ptr->mutex) == 0);
}
int main()
{
synchronized_counter* sc_ptr = create_synchronized_counter();
// I need to increment this counter three times in succesion without having
// another thread increment it in between. Therefore, I acquire a lock
// before beginning.
assert(pthread_mutex_lock(&sc_ptr->mutex) == 0);
synchronized_increment(sc_ptr);
synchronized_increment(sc_ptr);
synchronized_increment(sc_ptr);
assert(pthread_mutex_unlock(&sc_ptr->mutex) == 0);
return 0;
}
编辑:
我想用一个简单的例子来问这个问题,但也许它太简单了。这就是我想象的更现实的场景:我有一个堆栈数据结构,可以被多个线程访问。特别是,有时一个线程会从堆栈中弹出 n 个元素,但它必须一次全部完成(中间没有另一个线程从堆栈中压入或弹出)。设计问题的关键是我是否应该让客户端自己管理使用非递归互斥锁锁定堆栈,或者让堆栈提供同步的、简单的方法以及递归互斥锁,客户端可以使用这些方法进行多个原子事务也是同步的。
【问题讨论】:
-
如果您不使用具有递归功能的互斥锁,您必须确保您的代码不会递归。当您处理自己的代码时,这很容易实现,但是当您导出库时,您不知道它将调用您的代码,因此提供具有递归功能的函数可能更通用。我看不到任何其他用途
-
@geckos 说得好。在这样的方法中,我很难决定是否应该由客户端来确保同步访问,或者是否应该由我提供。在这种情况下,我将使用它的代码,所以我认为拥有一个非递归互斥体就可以完成这项工作。
-
如果做 API 我会导出两个函数,一个支持递归,另一个不支持。递归互斥锁可能比非递归互斥锁有更多的开销,但是,如果我有一个可以通过递归更好地处理的问题,我会使用递归互斥锁,直到它们被证明是一个问题。我还听说过早的优化是万恶之源,如果您的问题不是递归的,请使用非递归互斥锁:)
标签: c mutex recursive-mutex