2

我正在读史蒂文斯的书:apue。2e。我遇到了一个关于互斥锁的问题。

先来看一段代码:(以下代码来自apue 2e的图11.10)

#include <stdlib.h>
#include <pthread.h>

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

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

    if((fp = malloc(sizeof(struct foo))) != NULL){
        fp->f_count = 1;
        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); /* step 1 */
        pthread_mutex_destroy(&fp->f_lock); /* step 2 */
        free(fp);
    }else{
        pthread_mutex_unlock(&fp->f_lock);
    }
}

假设我们有两个线程:thread1 和 thread2。thread1 正在运行。

它调用函数 foo_rele 并完成了第 1 步的执行(见上文),并准备执行第 2 步。

但是,此时会发生上下文切换。并且thread2开始执行。

thread2 锁定锁 fp->f_lock 然后做一些事情。但在thread2解锁之前,上下文切换再次发生。

thread2 停止执行,thread1 开始执行。thread1 销毁锁,我们知道所有错误生成。

所以,我的问题是:上面提到的情况有可能发生吗?我们如何避免它,是否有任何接口(API)可以原子地解锁和销毁互斥锁​​?

4

2 回答 2

1

我同意 usr (+1),设计是错误的。

一般来说,在一个线程可以销毁任何东西之前,它必须确定所有其他线程都已完成使用它。这需要某种其他形式的线程间同步。您需要的是一种方法,线程 1 可以告诉线程 2 需要释放资源,并让线程 2 确认这一点,以便线程 1 可以调用 foo_rele() 确定线程 2 不会尝试使用 fp以后再。或者,线程 2 告诉线程 1 它不再需要 fp 并且线程 1 调用 foo_rele() 作为结果。

这可以通过多种方式完成。一种是线程1在锁的保护下设置一个标志,然后等待线程2退出。同时,线程 2 最终绕过来看到标志,然后退出。这会释放线程 1,然后调用 foo_rele()。另一种方式是通过管道传递消息(我首选的线程间同步方式);类似于线程 1 -> 线程 2,“请停止使用 fp”:线程 2 -> 线程 1,“好的”(当然,基于枚举更好定义的消息集是可取的,但你明白我的意思) .

你永远不能拥有的是线程 1 在没有线程 2 的情况下调用 foo_rele() 或者知道它永远不能再接触 fp 或者线程 2 已经退出。

于 2013-08-25T18:13:34.303 回答
1

这不是精心设计的,因为 thread2 访问了一个可能已经被销毁的对象。在启动 thread2之前添加一个引用(增量f_count) 。这样,thread2 就已经开始保证对象在运行时是稳定的。

于 2013-08-25T17:52:17.743 回答