我正在研究C中的一个项目,该项目涉及通过覆盖pthread.h来创建用户级线程库。我目前正在研究互斥函数。
在我的实现中,我将互斥锁存储为结构的链接列表。我想在pthread_mutex_init中做的是将指针存储到pthread_mutex_t互斥变量中新创建的互斥锁元素。这可能吗?这是我的一些代码,因此您可以了解我正在尝试做什么:
typedef struct mutexList
{
int mid;
struct mutexList *prevMutex;
struct mutexList *nextMutex;
int locked;
struct queueItem *owner;
struct blockedThread *blockedHead;
struct blockedThread *blockedTail;
} mutexElement;
int mutexCounter = 0;
mutexElement *mutexHead = NULL;
mutexElement *mutexTail = NULL;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
mutexElement *newMutex = (mutexElement *) malloc(sizeof(mutexElement));
fprintf(stdout, "***LOG: Creating new mutex.\n");
if(newMutex == NULL)
return ENOMEM;
newMutex->mid = mutexCounter;
mutexCounter++;
newMutex->locked = 0;
newMutex->nextMutex = NULL;
newMutex->blockedHead = NULL;
newMutex->blockedTail = NULL;
if(mutexHead == NULL)
mutexHead = newMutex;
if(mutexTail != NULL)
mutexTail->nextMutex = newMutex;
mutexTail = newMutex;
mutex = (&newMutex);
return 0;
}
答案 0 :(得分:1)
我认为您的总体想法可行,但您的特定示例至少存在一些问题:
mutexCounter
,mutexHead
和mutexTail
项目未以线程安全方式处理(此类库的重要属性)mutex = (&newMutex);
毫无意义 - mutex
是一个参数,是用户参数的副本。当你的函数返回时,mutex
就不复存在了;它不会返回给调用者。&newMutex
返回给用户也是一个问题:newMutex
是一个局部变量,它将在函数返回时到期。将指针存储在应该超过当前函数调用持续时间的内容中将不起作用。请注意,我并不是说上面是一个详尽无遗的问题列表 - 它们只是从蝙蝠中跳出来的。
您可以将pthread_mutex_t
类型设为struct mutexList*
的typedef,并设置newMutex
的值(不 newMutex
的地址)在*mutex
中,因为mutex
将是struct mutexList**
。用户的pthread_mutex_t
变量将包含指向链接列表中结构的指针。
如果用户忽略调用pthread_mutex_destroy()
并让他的pthread_mutex_t
变量超出范围,那么分配的struct mutexList
元素将永远不会被释放,但这是一个用户错误,你真的无能为力。
就互斥初始化而言,还有一个很大的问题,你还需要考虑 - 静态初始化。允许用户执行以下操作:
// the following is at file scope, so it has static duration
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
在这种情况下,用户不需要调用pthread_mutex_init()
- 事实上,这样做是错误的。您的库需要检测并执行首次使用时(或等效的)这些静态初始化的互斥锁所需的任何适当的动态初始化。并且你必须以线程安全的方式进行 - 第一次使用不仅允许在任意线程上完成,这些静态互斥锁的主要用例之一是允许在互斥上立即争用互斥锁(例如通过碰巧遇到它的第一个线程执行更复杂的其他静态数据初始化。
总而言之,我认为保持容器的私有互斥数据结构并使用户的pthread_mutex_t
变量基本上是指向容器中相应项的指针(或其他间接引用)的一般方法很好。但是,有许多复杂的细节 - 特别是确保您的函数是线程安全的 - 需要考虑和设计。
这个例子中存在的各种问题表明到目前为止设计还没有给出足够的思考。那可能没关系 - 也许你刚刚开始它;只是不要低估对细节的关注,你需要提供这些东西。
答案 1 :(得分:0)
由于用户可以自由地将它们的互斥锁声明为自动变量,或者将它们声明为指向从例如自动变量分配的内存的指针。 malloc(3)
,我对将互斥结构存储在全局链表上持怀疑态度 - 听起来很可能其中一个会超出范围并导致垃圾。 (当然,用户应该销毁他们的互斥体,但我不想相信那么远。)
请考虑pthread_mutex_init(3posix)
联机帮助页中的以下代码:
static pthread_mutex_t foo_mutex;
void foo_init()
{
pthread_mutex_init(&foo_mutex, NULL);
}
此代码使用static
- 已分配foo_mutex
,但也可以auto
- 已分配;当你创建一个新的mutexElement
并尝试通过*mutex
指针返回时,你的代码将不适用于这种情况......
那就是说,我认为你要找的东西很简单:
int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *attr) {
m->base_address = m;
/* ... your other work ... */
}