我在读史蒂文斯的书: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)可以原子地解锁和销毁互斥锁?
答案 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已经退出。