gpt4 book ai didi

c++ - 为什么 pthread_cond_timedwait 文档谈论 "unavoidable race"?

转载 作者:可可西里 更新时间:2023-11-01 18:35:57 24 4
gpt4 key购买 nike

The POSIX documentation (IEEE 1003.1, 2013) 对于 pthread_cond_timedwait 函数说:

It is important to note that when pthread_cond_wait() and pthread_cond_timedwait() return without error, the associated predicate may still be false. Similarly, when pthread_cond_timedwait() returns with the timeout error, the associated predicate may be true due to an unavoidable race between the expiration of the timeout and the predicate state change.

(强调我的)

我们都知道条件变量控制的谓词应该在 while 循环中检查,并且可能会出现虚假唤醒。但我的问题是关于unavoidable 这个词——这是一个强词。为什么这样的竞赛不可避免?

注意,如果不存在这样的竞争,我们可以只检查 pthread_cond_timedwait 是否超时;而不是再次检查谓词,然后才处理超时条件。 (当然,假设我们仅在 1) 持有互斥量和 2) 谓词实际更改时收到信号。)

在保持“用户互斥量”的情况下,如果我们被超时唤醒或收到信号,是否足以自动检查?


例如,让我们考虑在 POSIX 变量之上构建的条件变量的实现。 (省略了错误处理和初始化,可以填补明显的空白)。

class CV 
{
pthread_mutex_t mtx;
pthread_cond_t cv;
int waiters; // how many threads are sleeping
int wakeups; // how many times this cv got signalled

public:
CV();
~CV();

// returns false if it timed out, true otherwise
bool wait(Mutex *userMutex, struct timespec *timeout)
{
pthread_mutex_lock(&mtx);

waiters++;
const int oldWakeups = wakeups;

userMutex->unlock();

int ret; // 0 on success, non-0 on timeout

for (;;) {
ret = pthread_cond_timedwait(&mtx, &cv, timeout);
if (!(ret == 0 && wakeups == 0))
break; // not spurious
}

if (ret == 0) // not timed out
wakeups--;

pthread_mutex_unlock(&mtx);

userMutex->lock();

pthread_mutex_lock(&mtx);
waiters--;
if (ret != 0 && wakeups > oldWakeups) {
// got a wakeup after a timeout: report the wake instead
ret = 0;
wakeups--;
}
pthread_mutex_unlock(&mtx);

return (ret == 0);
}

void wake()
{
pthread_mutex_lock(&mtx);
wakeups = min(wakeups + 1, waiters);
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);
}
};

可以证明

  • 如果 CV::wait 报告超时,那么我们没有收到信号,因此谓词没有改变;还有那个
  • 如果超时到期但我们在返回到持有用户互斥锁的用户代码之前收到信号,那么我们报告唤醒

上面的代码是否包含一些严重的错误?如果不是,说这场比赛是不可避免的标准是错误的,还是它必须做一些我错过的其他假设?

最佳答案

首先,请注意这有一个通常很危险的部分:

pthread_mutex_unlock(&mtx);
// Trouble is here
userMutex->lock();

pthread_mutex_lock(&mtx);

在注释点,任何事情都有可能发生。你没有锁。条件变量的强大之处在于它们总是要么持有锁,要么等待。

然后是手头的问题,不可避免的比赛

if (ret != 0 && wakeups > oldWakeups) {
// got a wakeup after a timeout: report the wake instead
ret = 0;
wakeups--;
}

无法保证一堆 pthread_cond_t 的等待会以何种顺序被唤醒,这会对您的计数造成严重破坏

Thread1           Thread2        Thread3
{lock userMtx in calling code}
{lock mtx}
waiters++ (=1)
oldWakeups = 0
{unlock userMtx }
wait {unlock mtx}
{lock userMtx in calling code}
{lock mtx}
signal_all
wakeups = 1
{unlock mtx}
{unlock userMtx in calling code}
timeout(unavoid. racecase) {lock mtx}
{unlock mtx}
{lock userMtx in calling code}
{lock mtx}
waiters++ (=2)
oldWawkupes = 1
{unlock userMtx }
wait {unlock mtx}

timeout {lock mtx}
{unlock mtx}
{lock userMtx}
{lock mtx}
waiters-- (=1)
wakeups-- (=0)*
{unlock mtx}
{unlock userMtx in calling code}
{lock userMtx}
{lock mtx}
waiters--(=0)
wakeups == oldWakeups (=0)
{unlock mtx}
{unlock userMtx in calling code}

此时,在线程 1 上,oldWakeups = wakeups,因此对不可避免的竞争情况的检查未能注意到竞争情况,从而重新创建了不可避免的竞争情况。这是由于线程 3 窃取了线程 1 的信号,使线程 3(真正的超时)看起来像一个信号,而线程 1(竞争信号/超时)看起来像一个超时

关于c++ - 为什么 pthread_cond_timedwait 文档谈论 "unavoidable race"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18642385/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com