如何使用 pthread_mutex_destroy 在 Linux 中安全正确地销毁互斥锁​​?

问题描述:

我了解了 APUE 3rd,11.6.1 Mutexes,本章有一个关于锁定和解锁互斥锁的例子:

I read about APUE 3rd, 11.6.1 Mutexes, there is an example about lock and unlock a mutex in this chapter:

struct foo {
    int             f_count;
    pthread_mutex_t f_lock;
    int             f_id;
    /* ... more stuff here ... */
};

struct foo *
foo_alloc(int id) /* allocate the object */
{
    struct foo *fp;

    if ((fp = malloc(sizeof(struct foo))) != NULL) {
        fp->f_count = 1;
        fp->f_id = id;
        if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
            free(fp);
            return(NULL);
        }
        /* ... continue initialization ... */
    }
    return(fp);
}

void
foo_hold(struct foo *fp) /* add a reference to the object */
{
    pthread_mutex_lock(&fp->f_lock);
    fp->f_count++;
    pthread_mutex_unlock(&fp->f_lock);
}

void
foo_rele(struct foo *fp) /* release a reference to the object */
{
    pthread_mutex_lock(&fp->f_lock);
    if (--fp->f_count == 0) { /* last reference */
        pthread_mutex_unlock(&fp->f_lock);
        pthread_mutex_destroy(&fp->f_lock);
        free(fp);
    } else {
        pthread_mutex_unlock(&fp->f_lock);
    }
}

foo_rele中,pthread_mutex_unlockpthread_mutex_destroy之间存在竞争条件:B线程可以调用线程中pthread_mutex_unlockpthread_mutex_destroy之间的>pthread_mutex_lock,这将导致未定义的行为(尝试销毁锁定的互斥锁会导致未定义行为").

In foo_rele, there is a race condition between pthread_mutex_unlock and pthread_mutex_destroy: B thread can call pthread_mutex_lock between pthread_mutex_unlock and pthread_mutex_destroy in A thread which will cause undefined behavior ("Attempting to destroy a locked mutex results in undefined behavior").

我说得对吗?如果我是对的,那么,如何使其正常工作或如何使用 pthread_mutex_destroy 安全正确地销毁 Linux 中的互斥锁?

Am I right? If I'm right, then, how to make it work right or how to safely and correctly destroy a mutex in Linux using pthread_mutex_destroy?

pthread_mutex_destroy() 的 POSIX 规范说:

The POSIX spec for pthread_mutex_destroy() says:

销毁未锁定的已初始化互斥锁应该是安全的.

It shall be safe to destroy an initialized mutex that is unlocked.

这意味着如果线程B在foo_rele()if语句的else子句中调用pthread_mutex_unlock() 那么线程 A 调用 pthread_mutex_destroy() 是安全的,因为它只能在线程 B 的 pthread_mutex_unlock() 调用解锁互斥锁之后才能到达那里.

Which means that if thread B calls pthread_mutex_unlock() in the else clause of the if statement in foo_rele() then it's safe for thread A to call pthread_mutex_destroy() because it can only have gotten there after thread B's pthread_mutex_unlock() call has unlocked the mutex.

所有这些都假设引用计数是正确的,这样在线程 A 解锁互斥锁后,其他线程无法从 0 -> 1 增加计数.换句话说,在引用计数下降到 0 时,不可能有另一个线程可能调用 foo_hold().

All of this is assuming that the reference counting is correct, such that some other thread cannot increment the count from 0 -> 1 after thread A has unlocked the mutex. In other words, at the point where the refcount drops to 0, there can't be another thread that might possibly call foo_hold().

APUE 在示例代码之后的解释中提到了这一点:

APUE mentions this in the explanation right after the example code:

在这个例子中,我们忽略了线程在调用 foo_hold 之前如何找到一个对象.即使引用计数为零,如果另一个线程在调用 foo_hold 时在互斥锁上被阻塞,foo_rele 释放对象的内存也是错误的.我们可以通过确保在释放内存之前找不到对象来避免这个问题.我们将在以下示例中了解如何执行此操作.

In this example, we have ignored how threads find an object before calling foo_hold. Even though the reference count is zero, it would be a mistake for foo_rele to free the object’s memory if another thread is blocked on the mutex in a call to foo_hold. We can avoid this problem by ensuring that the object can’t be found before freeing its memory. We’ll see how to do this in the examples that follow.