代码之家  ›  专栏  ›  技术社区  ›  shodanex

pthread_cond_wait与信号量

  •  48
  • shodanex  · 技术社区  · 17 年前

    使用的优缺点是什么 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);
    }
    

    每种方法的优缺点是什么?

    5 回复  |  直到 10 年前
        1
  •  61
  •   Steve Jessop    17 年前

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

    条件变量在某些方面更宽容一些。例如,您可以使用cond_broadcast唤醒所有服务员,而制作人不知道有多少服务员。如果你在没有人等待的情况下发出condvar信号,那么什么都不会发生。如果你不知道是否会有听众感兴趣,这很好。这也是为什么监听器在等待之前应该始终检查互斥体的状态——如果他们没有检查,那么他们可能会错过一个信号,直到下一个信号才醒来(这可能永远不会)。

    因此,条件变量适合通知相关方状态已更改:您获取互斥体,更改状态,用信号(或广播)发送condvar并释放互斥体。如果这描述了你的问题,那么你就在condvar领域。如果不同的听众对不同的州感兴趣,你可以直接广播,他们会依次醒来,弄清楚他们是否找到了他们想要的州,如果没有,再等一次。

    用互斥体和信号量尝试这种事情确实非常困难。当你想获取互斥体,检查一些状态,然后等待信号量的变化时,问题就来了。除非你能原子性地释放互斥体并等待信号量(在pthreads中你不能),否则你最终会在持有互斥体的同时等待信号量。这会阻止互斥体,这意味着其他人无法利用它来做出你关心的更改。因此,您可能会倾向于根据您的特定要求添加另一个互斥体。也许还有另一个信号。结果通常是错误的代码和有害的种族条件。

    条件变量避免了这个问题,因为调用cond_wait会自动释放互斥体,将其释放给其他人使用。在cond_wait返回之前,互斥体被恢复。

    IIRC可以仅使用信号量来实现一种condvar,但如果你要实现的与condvar一起使用的互斥体需要具有trylock,那么这是一个严重的问题,并且超时等待已经结束。不推荐。因此,不要想当然地认为你可以用condvar做的任何事情都可以用信号量来完成。当然,互斥体可以具有信号量所缺乏的良好行为,主要是避免优先级反转。

        2
  •  19
  •   Nisse Engström sting_roc    10 年前

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

    例如,假设您有一些需要互斥体的代码,称为 m 然而,它需要等待其他线程完成任务,因此它等待一个名为 s .现在任何需要的线程 男性 被阻止运行,即使线程 男性 正在等待 s 这类情况可以用条件句来解决。当你等待一个条件时,当前持有的互斥体会被释放,这样其他线程就可以获取互斥体。所以回到我们的例子,假设条件 c 被使用而不是 s .我们的线程现在获得 男性 ,然后有条件地等待 c 。这将释放 男性 因此其他线程可以继续。什么时候 c 变得可用, 男性 重新获得,我们的原始线程可以愉快地继续前进。

    条件变量还允许您让 所有 线程等待条件变量通过 pthread_cond_broadcast 。此外,它还允许您执行 定时等待 所以你不会永远等待。

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

        3
  •  5
  •   Blaisorblade    17 年前

    第二段很生动,不要这样做。

    其他答案对相对优点进行了很好的讨论;我只是补充一下 pthread_cond_broadcast 是条件变量的一个明显优势。

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

    事实上,在第二段代码中,您没有任何锁来保护cam的读取->状态,因此它是通过数据竞争访问的。在这个特定的例子中,大多数平台都会让你逃脱惩罚,但根据POSIX和下一个C/C++标准的内存模型,这具有未定义的语义。

    事实上,如果另一个线程分配了一个新的凸轮结构并覆盖了凸轮,那么真正的竞争条件是可能的;等待线程可能会看到“cam”指针的更新,而看不到cam的初始化->状态。事实上,在这种情况下和一般情况下,第二个片段是在自找麻烦。

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

        4
  •  0
  •   Javier    17 年前

    在第二个代码片段中,您多次获得锁,但从未释放它。

    一般来说,你放弃的状态可以完全由信号量表示,然后你就可以使用它。锁结构的大小较小,检查/设置/释放所需的原子操作也较少。

    否则,如果状态很复杂,并且代码的不同部分在同一变量的不同条件下等待(例如,这里你想要x<10;那里你想要y>x),请使用cond_wait。