条件变量和 mutex_unlock
Condition variables and mutex_unlock
代码:
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
sleep(1);
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&count_threshold_cv,&mutex);
sleep(1);
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
现在我们有一个 mutex_unlock
函数要在每个线程中执行。和一个锁定的互斥量。为什么这不会导致 undefined behaviour
。由于两个线程都试图解锁同一个互斥锁,这导致一个线程试图解锁一个已经解锁的互斥锁。
编辑:pthread_cond_wait
释放 mutex
供第二个线程使用。现在考虑第二个线程执行 pthread_cond_signal
,这导致第一个线程重新获取 mutex
。现在我们有两个具有相同 mutex
锁的线程,因为由于 'sleep' 函数,两个线程都没有执行 mutex_unlock
。我的理解有误吗?
pthread_mutex_lock()
如果要锁定的互斥体已经被锁定,则阻塞。它 returns 如果互斥量被解锁。
pthread_cond_wait()
在开始等待时解锁互斥锁,在返回之前锁定。如果有问题的互斥量仍处于锁定状态,则返回可能会延迟。然后返回将延迟,直到互斥量被解锁。
将以上内容放在一起并将其应用到您展示的代码中,您会发现每个线程函数都很好地以锁定开始,然后是解锁(依此类推),所以一切都很好。
参考示例代码:pthread_cond_wait()
returns when inc_func()
had call pthread_mutex_unlock()
.
要成功处理示例代码描述的场景,您需要考虑两种特殊情况
- 信号先到的情况
- 所谓的 "spurious wake-ups" 的情况,即
pthread_cond_wait()
在没有收到信号的情况下返回。
要处理这两种情况,每个条件都应该有一个监视变量。
pthread_mutex_t mutex = ...
pthread_cond_t count_threshold_cv = ...
int signalled = 0;
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
signalled = 1;
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
while (0 == signalled)
{
pthread_cond_wait(&count_threshold_cv,&mutex);
}
pthread_mutex_unlock(&mutex);
}
int main(void)
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
如果订单确实是
watch
首先运行并锁定(而 inc_func
等待)
watch
有互斥等待使用 pthread_cond_wait
解锁互斥并根据 documentation
These functions atomically release mutex and cause the calling thread
to block on the condition variable cond;
这允许以下
inc_func
获取互斥量然后发信号(此时互斥量尚未释放)
inc_func
释放互斥量
- 因为互斥量已释放且互斥量已解锁
watch
的执行恢复并已根据 documentation: 锁定互斥量
Upon successful return, the mutex has been locked and is owned by the calling thread.
- 接下来是互斥量的合法释放。
你没有考虑的场景是如果inc_func
的代码先执行而不切换到watch
会怎样
inc_func
锁定互斥体
inc_func
发出信号但没有人发出信号,这没关系。
inc_func
解锁互斥
watch
锁定互斥体
- 然后等待条件变量,但没有人向它发出信号,因此它将永远等待。
代码:
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
sleep(1);
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&count_threshold_cv,&mutex);
sleep(1);
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
现在我们有一个 mutex_unlock
函数要在每个线程中执行。和一个锁定的互斥量。为什么这不会导致 undefined behaviour
。由于两个线程都试图解锁同一个互斥锁,这导致一个线程试图解锁一个已经解锁的互斥锁。
编辑:pthread_cond_wait
释放 mutex
供第二个线程使用。现在考虑第二个线程执行 pthread_cond_signal
,这导致第一个线程重新获取 mutex
。现在我们有两个具有相同 mutex
锁的线程,因为由于 'sleep' 函数,两个线程都没有执行 mutex_unlock
。我的理解有误吗?
pthread_mutex_lock()
如果要锁定的互斥体已经被锁定,则阻塞。它 returns 如果互斥量被解锁。pthread_cond_wait()
在开始等待时解锁互斥锁,在返回之前锁定。如果有问题的互斥量仍处于锁定状态,则返回可能会延迟。然后返回将延迟,直到互斥量被解锁。
将以上内容放在一起并将其应用到您展示的代码中,您会发现每个线程函数都很好地以锁定开始,然后是解锁(依此类推),所以一切都很好。
参考示例代码:pthread_cond_wait()
returns when inc_func()
had call pthread_mutex_unlock()
.
要成功处理示例代码描述的场景,您需要考虑两种特殊情况
- 信号先到的情况
- 所谓的 "spurious wake-ups" 的情况,即
pthread_cond_wait()
在没有收到信号的情况下返回。
要处理这两种情况,每个条件都应该有一个监视变量。
pthread_mutex_t mutex = ...
pthread_cond_t count_threshold_cv = ...
int signalled = 0;
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
signalled = 1;
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
while (0 == signalled)
{
pthread_cond_wait(&count_threshold_cv,&mutex);
}
pthread_mutex_unlock(&mutex);
}
int main(void)
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
如果订单确实是
watch
首先运行并锁定(而inc_func
等待)watch
有互斥等待使用pthread_cond_wait
解锁互斥并根据 documentation
These functions atomically release mutex and cause the calling thread to block on the condition variable cond;
这允许以下
inc_func
获取互斥量然后发信号(此时互斥量尚未释放)inc_func
释放互斥量- 因为互斥量已释放且互斥量已解锁
watch
的执行恢复并已根据 documentation: 锁定互斥量
Upon successful return, the mutex has been locked and is owned by the calling thread.
- 接下来是互斥量的合法释放。
你没有考虑的场景是如果inc_func
的代码先执行而不切换到watch
inc_func
锁定互斥体inc_func
发出信号但没有人发出信号,这没关系。inc_func
解锁互斥watch
锁定互斥体- 然后等待条件变量,但没有人向它发出信号,因此它将永远等待。