如何以原子方式解锁和销毁互斥锁

时间:2013-08-25 17:31:12

标签: c multithreading unix mutex

我在读史蒂文斯的书:apue。 2E。我遇到了一个关于Mutex的问题。

让我们先看一下代码:(以下代码来自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-&gt; f_lock然后执行某些操作。但是在thread2解锁之前,再次发生上下文切换。

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

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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

我同意usr(+1),设计错了。

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

这可以通过多种方式实现。一种是线程1在锁的保护下设置一个标志,然后等待线程2退出。同时,线程2最终会看到该标志,然后退出。这将释放线程1,然后调用foo_rele()。另一种方式是通过管道传递消息(我首选的线程间同步方式);沿线1 - &gt;的东西线程2,“请停止使用fp”:线程2 - &gt;线程1,“确定”(当然,基于枚举的更好定义的消息集是可取的,但你得到了我的意思)。

你不能拥有的是线程1在没有线程2的情况下调用foo_rele()要么知道它必须永远不再触摸fp,要么线程2已经退出。