2008-09-16 68 views
41

使用pthread_cond_wait或使用信号量有什么优点/缺点? 我等待这样的状态变化:pthread_cond_wait与信号量

pthread_mutex_lock(&cam->video_lock); 
while(cam->status == WAIT_DISPLAY) { 
    pthread_cond_wait(&cam->video_cond, &cam->video_lock); 
} 
pthread_mutex_unlock(&cam->video_lock); 

使用正确初始化旗语,我想我能做到这一点是这样的:

while(cam->status == WAIT_DISPLAY) { 
    sem_wait(&some_semaphore); 
} 

什么是每种方法的利弊?

+0

我刚刚意识到“正确初始化旗语”是不明确的。信号量是否设置为1或0?我会说它应该被设置为0.然后,信号量是否保护凸轮 - >状态? – Blaisorblade 2014-06-01 22:45:33

+0

在其他答案中提到的第二个片段中,当互斥体未被释放时,您的线程将被阻塞。所以你的`sem_wait`方法永远不会返回,因为没有其他线程可以获得互斥体并调用`sem_signal`。但是,如果我在`sem_wait`之前释放互斥并在等待之后再次请求它,我不会发生什么。我知道这些步骤不是原子的,那么会发生什么? – sevenkplus 2016-10-03 15:31:54

回答

58

尽管信号量还有其他用途,但它适用于生产者 - 消费者模型。您的程序逻辑负责确保为等待次数提供适当的帖子数量。如果你发布一个信号而没有人等待,那么当他们等待时,他们立即继续。如果您的问题可以用信号量的计数值来解释,那么使用信号量应该很容易解决。

条件变量在某些方面有点宽容。例如,您可以使用cond_broadcast唤醒所有服务员,而制片人不知道有多少人。如果你没有人在等待它,cond_signal condvar然后没有反应。如果你不知道是否有兴趣的听众,这很好。这也是为什么聆听者应该总是在等待之前检查互斥状态 - 如果他们不这样,他们就会错过一个信号,直到下一个(可能永远不会)醒来。

所以一个条件变量适用于通知感兴趣的各方状态已经改变:你获得互斥量,改变状态,信号(或广播)condvar并释放互斥量。如果这描述你的问题,你在condvar领土。如果不同的听众对不同的状态感兴趣,你可以直接播放,然后每个人都会醒来,找出他们是否已经找到了他们想要的状态,如果不再等待。

用互斥量和信号量来尝试这种事情确实非常困难。问题出现在你想要获取互斥锁时,检查一些状态,然后等待信号量发生变化。除非你可以自动释放互斥量并等待信号量(在pthread中你不能),否则最终等待信号量同时持有互斥量。这会阻止互斥体,这意味着其他人无法将其用于执行您关心的更改。所以你会被诱惑添加另一个互斥体,这取决于你的具体要求。也许是另一个信号量。结果通常是不正确的代码,有害的竞态条件。

条件变量可以避免这个问题,因为调用cond_wait会自动释放互斥锁,释放它以供其他人使用。在cond_wait返回之前,互斥体会重新获得。

IIRC有可能仅使用信号量来实现一种condvar,但是如果您要实现与condvar一起使用的互斥锁需要使用trylock,那么它是一个严重的头部划伤,并且定时等待。不建议。所以不要认为你可以用condvar做什么都可以用信号量来完成。此外,当然互斥体可以有很好的信号量缺乏行为,主要是避免优先级倒置。

0

在你的第二个片段中,你获得了多次锁定,永远不会释放它。

一般来说,你所说的状态可以完全用信号量表示,那么你可以使用它。锁结构体积较小,并且需要较少的原子操作来检查/设置/释放。否则,如果状态复杂,并且代码的不同部分在相同变量的不同条件下等待(例如,此处需要x < 10;那么您希望y> x),请使用cond_wait。否则,请使用cond_wait。

+0

第二个片段是正确的 - 唤醒总是由某个人发出信号灯引起的。 – Blaisorblade 2009-01-12 09:04:46

+0

@Blaisorblade,醒来是正确的,状态检查是否定的,因为它没有被保护。 – 2014-05-28 03:59:29

19

条件允许你做一些信号量不会的事情。

例如,假设您有一些需要互斥体的代码,称为m。然而,它需要等到其他线程完成任务后,才会等待一个名为s的信号量。现在任何需要m的线程都会被阻止运行,即使线程m正在等待s。这些情况可以使用条件来解决。当你等待一个条件时,当前持有的互斥锁被释放,所以其他线程可以获得互斥锁。回到我们的例子,假设使用条件c而不是s。我们的线程现在获取m,然后有条件地等待c。这会释放m以便其他线程可以继续。当c变得可用时,m被重新获得,并且我们的原始线程可以继续愉快地沿着它的方式。

条件变量还允许您让全部线程等待条件变量通过pthread_cond_broadcast继续。此外,它还允许您执行定时等待,因此您不会永远等待。

当然,有时候你不需要条件变量,所以根据你的要求,其中一个可能会更好。

4

第二个片段是活泼的,不要这样做。

其他答案对相对优点有很好的讨论;我只是补充说明pthread_cond_broadcast是条件变量的明显优势。

除此之外,我只是更习惯于为变量指定条件,因为它们是您在Java中使用的变量,即使它们在检查共享标志时帮助您避免竞争。

确实,在第二个片段中,您没有任何锁定来保护cam-> status的读取,因此可以通过数据竞争来访问它。在这个特例中,大多数平台都可以让你摆脱这种困境,但是这种平台具有未定义的语义,POSIX和下一个C/C++标准的内存模型。

事实上,如果另一个线程分配一个新的凸轮结构并覆盖凸轮,则可能存在真实的竞争条件;等待的线程可能会看到'cam'指针的更新,而没有看到cam-> status的初始化。事实上,在这种情况下和总体上,第二个片段正在寻求麻烦。

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/