2

我正在使用 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;
}
4

2 回答 2

2

我认为您的一般想法可以工作,但是您的特定示例至少存在一些问题:

  • 您的mutexCounter,mutexHeadmutexTail项目没有以线程安全的方式处理(这种库的一个重要属性)
  • 该行mutex = (&newMutex);毫无意义 -mutex是一个参数,它是用户参数的副本。当你的函数返回时,mutex它就不再存在了;它不会返回给调用者。
  • 甚至一开始就试图返回&newMutex给用户也是一个问题:newMutex是一个局部变量,会在函数返回时过期。将指向它的指针存储在应该超过当前函数调用持续时间的东西中是行不通的。

请注意,我并不是说以上是详尽的问题列表——它们只是突然出现的问题。

您可以将pthread_mutex_t类型设为 a 的 typedefstruct mutexList*并将newMutex不是的地址newMutex)的值放入其中,*mutex因为mutex将是 a 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变量本质上是指向容器中适当项目的指针(或其他一些间接引用)的一般方法很好。然而,有很多复杂的细节——尤其是确保你的函数是线程安全的——需要考虑和设计。

示例中存在的各种问题表明,到目前为止,该设计还没有得到足够的考虑。那可能没问题——也许你刚刚开始;只是不要低估你需要给予这些东西的对细节的关注。

于 2012-04-18T09:21:30.830 回答
0

由于用户可以自由地将他们的互斥体声明为自动变量或将它们声明为指向从 eg 分配的内存的指针malloc(3),我对将互斥体结构存储在全局链表上持怀疑态度——听起来它们中的一个很可能会消失。范围并导致垃圾。(当然,用户应该销毁他们的互斥锁,但我不想相信那么远。)

考虑手册页中的以下代码pthread_mutex_init(3posix)

static pthread_mutex_t foo_mutex;

void foo_init()
{
    pthread_mutex_init(&foo_mutex, NULL);
}

此代码使用static-allocated foo_mutex,但也可以是auto-allocated ;当您创建一个新的mutexElement并尝试通过*mutex指针分配回时,您的代码将不适用于这种情况......

也就是说,我认为您要查找的内容非常简单:

int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *attr) {
    m->base_address = m;
    /* ... your other work ... */
}
于 2012-04-18T02:53:59.170 回答