我pthread_cond_wait(&cond_t, &mutex);在我的程序中使用,我想知道为什么这个函数需要作为第二个参数mutex变量.
是否pthread_cond_wait()在开始时解锁互斥锁(执行开始pthread_cond_wait())然后在完成时(就在离开之前pthread_cond_wait())锁定?
Who*_*aig 119
关于条件变量及其用法的主题有很多文本,所以我不会给你带来大量丑陋的细节.它们存在的原因是允许您通知谓词状态的更改.以下对于理解条件变量及其互斥关联的正确使用至关重要:
pthread_cond_wait()同时解锁互斥锁并开始等待条件变量发出信号.因此,在调用互斥锁之前,必须始终拥有互斥锁的所有权.
pthread_cond_wait()在互斥锁被锁定的情况下返回,因此您必须解锁互斥锁,以便在完成后使用其他地方.返回是否因为条件变量已发出信号而发生是不相关的.您仍然需要检查您的谓词,无论是否考虑到潜在的虚假唤醒.
互斥的目的不是保护条件变量; 它是为了保护条件变量被用作信令机制的谓词.这是pthread条件变量及其互斥体最常被误解的习惯用法.条件变量不需要互斥保护; 谓词数据确实如此.将谓词视为外部状态,由条件变量/互斥体对的用户监视.
例如,等待布尔标志的一段微不足道但却明显错误的代码fSet:
bool fSet = false;
int WaitForTrue()
{
while (!fSet)
{
sleep(n);
}
}
Run Code Online (Sandbox Code Playgroud)
我应该明白主要问题是谓词fSet,根本没有受到保护.很多事情都可能出错.例如:从评估while-conditon到开始等待(或旋转,或其他)的时间,值可能已经改变.如果错过了那个改变通知,你就会不必要地等待.
我们可以稍微改变一下,所以至少谓词会以某种方式受到保护.修改和评估谓词的相互排除很容易提供(还有什么)互斥.
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
bool fSet = false;
int WaitForTrue()
{
pthread_mutex_lock(&mtx);
while (!fSet)
sleep(n);
pthread_mutex_unlock(&mtx);
}
Run Code Online (Sandbox Code Playgroud)
好吧,这看起来很简单..现在我们从不评估谓词而不首先获得它的独占访问权限(通过锁存互斥锁).但这仍然是一个主要问题.我们锁定了互斥锁, 但在循环结束之前我们永远不会释放它.如果其他人按照规则进行操作并在评估或修改之前等待互斥锁fSet,则在我们放弃互斥锁之前,他们永远无法执行此操作.在这种情况下唯一可以做到这一点的"人"就是我们.
那么为此添加更多图层呢?这会有用吗?
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
bool fSet = false;
int WaitForTrue()
{
pthread_mutex_lock(&mtx);
while (!fSet)
{
pthread_mutex_unlock(&mtx);
// XXXXX
sleep(n);
// YYYYY
pthread_mutex_lock(&mtx);
}
pthread_mutex_unlock(&mtx);
}
Run Code Online (Sandbox Code Playgroud)
嗯,是的,它会"奏效",但仍然没有好多少.之间的时间段XXXXX和YYYYY我们不拥有互斥锁(这是好的,因为我们还没有检查或修改fSet).但是在那个时期的任何时候,其他一些线程可以(a)获得互斥体,(b)修改fSet,以及(c)释放互斥体,在我们完成之前我们不会知道它的事情sleep(),一次又一次获得互斥体锁定,并循环进行另一次检查.
有有是一个更好的办法.不知怎的,应该有一种方法可以释放互斥锁并开始等待某种信号,告诉我们谓词的变化可能已经发生.同样重要的是,当我们收到该信号并返回到我们的代码时,我们应该已经拥有了授予我们访问权限以检查谓词数据的锁.这正是条件变量旨在提供的.
行动中的条件变量
输入条件变量+互斥量对.互斥锁保护对更改或检查谓词的访问,而条件变量建立了一个监视变化的系统,更重要的是,通过谓词互斥,以原子方式(无论如何,你这么做)这样做:
int WaitForPredicate()
{
// lock mutex (means:lock access to the predicate)
pthread_mutex_lock(&mtx);
// we can safely check this, since no one else should be
// changing it unless they have the mutex, which they don't
// because we just locked it.
while (!predicate)
{
// predicate not met, so begin waiting for notification
// it has been changed *and* release access to change it
// to anyone wanting to by unlatching the mutex, doing
// both (start waiting and unlatching) atomically
pthread_cond_wait(&cv,&mtx);
// upon arriving here, the above returns with the mutex
// latched (we own it). The predicate *may* be true, and
// we'll be looping around to see if it is, but we can
// safely do so because we own the mutex coming out of
// the cv-wait call.
}
// we still own the mutex here. further, we have assessed the
// predicate is true (thus how we broke the loop).
// take whatever action needed.
// You *must* release the mutex before we leave. Remember, we
// still own it even after the code above.
pthread_mutex_unlock(&mtx);
}
Run Code Online (Sandbox Code Playgroud)
对于其他一些线程来表示上面的循环,有几种方法可以做到,下面两个最受欢迎:
pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cv);
Run Code Online (Sandbox Code Playgroud)
其他方式...
pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);
Run Code Online (Sandbox Code Playgroud)
每个都有不同的内在行为,我邀请你做一些关于这些差异的功课,并确定哪个更适合特定情况.前者以引入可能无根据的唤醒为代价提供更好的程序流程.后者减少了这些唤醒,但代价是较少的背景协同作用.两者都可以在我们的示例中使用,您可以尝试各自如何影响您的等待循环.无论如何,有一件事是至关重要的,而且这两种方法都能完成这项任务:
除非互斥锁被锁定,否则切勿更改或检查谓词条件.曾经.
简单的监视线程
这种类型的操作在监视器线程中很常见,该线程作用于特定的谓词条件,(没有错误检查)通常看起来像这样:
void* monitor_proc(void *pv)
{
// acquire mutex ownership
// (which means we own change-control to the predicate)
pthread_mutex_lock(&mtx);
// heading into monitor loop, we own the predicate mutex
while (true)
{
// safe to check; we own the mutex
while (!predicate)
pthread_cond_wait(&cv, &mtx);
// TODO: the cv has been signalled. our predicate data should include
// data to signal a break-state to exit this loop and finish the proc,
// as well as data that we may check for other processing.
}
// we still own the mutex. remember to release it on exit
pthread_mutex_unlock(&mtx);
return pv;
}
Run Code Online (Sandbox Code Playgroud)
更复杂的监视器线程
修改此基本表单以考虑一个通知系统,该通知系统不需要您在获取通知后保持互斥锁被锁定,这会更加复杂,但不是很多.下面是一个监视器proc,一旦我们确定已经服务(可以这么说),它们在常规处理过程中不会保持互斥锁.
void* monitor_proc(void *pv)
{
// acquire mutex ownership
// (which means we own change-control to the predicate)
pthread_mutex_lock(&mtx);
// heading into monitor loop, we own the predicate mutex
while (true)
{
// check predicate
while (!predicate)
pthread_cond_wait(&cv, &mtx);
// some state that is part of the predicate to
// inform us we're finished
if (break-state)
break;
// TODO: perform latch-required work here.
// unlatch the mutex to do our predicate-independant work.
pthread_mutex_unlock(&mtx);
// TODO: perform no-latch-required work here.
// re-latch mutex prior to heading into wait
pthread_mutex_lock(&mtx);
}
// we still own the mutex. remember to release it on exit
pthread_mutex_unlock(&mtx);
return pv;
}
Run Code Online (Sandbox Code Playgroud)
哪里会有人使用类似的是?好吧,假设你的"谓词"是工作队列的"状态",还有一些标志告诉你停止循环和退出.在收到"不同"的通知后,您检查是否应该继续执行循环,并决定是否应该继续,从队列中弹出一些数据.修改队列需要锁定互斥锁(记住,它的"状态"是我们谓词的一部分).一旦我们弹出了我们的数据,我们在本地拥有它并且可以独立于队列状态处理它,所以我们释放互斥锁,做我们的事情,然后需要互斥量用于下一次复飞.有许多方法可以对上述概念进行编码,包括明智地使用pthread_cond_broadcast等等.但基本形式是可以理解的.
事实证明这比我希望的要长得多,但这是人们学习pthread编程的一个主要障碍,我觉得值得花费额外的时间/精力.我希望你能得到一些东西.
Lih*_*ihO 34
当第一个线程调用pthread_cond_wait(&cond_t, &mutex);它时,释放互斥锁并等待,直到条件cond_t被指示为完成且 mutex可用.
因此,当pthread_cond_signal在另一个线程中调用时,它不会"唤醒"等待的线程.mutex必须首先解锁,只有这样第一个线程才有可能获得锁定,这意味着"成功返回pthread_cond_wait互斥锁后,应该被锁定,并且应该由调用线程拥有."
是的它解锁,等待条件满足,然后等待它可以重新获得通过的互斥锁.
| 归档时间: |
|
| 查看次数: |
34228 次 |
| 最近记录: |