C++ 有多少互斥和cond变量?

C++ 有多少互斥和cond变量?,c++,pthreads,C++,Pthreads,我是pthread池上的wokring,将有五个独立的线程和一个队列。所有五个线程都在竞争从队列中获取作业,我知道我需要执行锁定/解锁和等待/发送信号的基本思想 但我不确定应该有多少互斥和cond变量。现在我只有一个互斥体和cond变量,所有五个线程都将使用它。一个互斥体和至少一个条件变量 一个互斥锁,因为有一个“东西”(即内存块)需要同步访问:所有工作线程和推动工作的线程之间的共享状态 对于一个或多个线程需要等待的条件,每个条件变量有一个条件变量。至少你需要一个条件变量来等待新的工作,这里的条

我是pthread池上的wokring,将有五个独立的线程和一个队列。所有五个线程都在竞争从队列中获取作业,我知道我需要执行锁定/解锁和等待/发送信号的基本思想


但我不确定应该有多少互斥和cond变量。现在我只有一个互斥体和cond变量,所有五个线程都将使用它。

一个互斥体和至少一个条件变量

一个互斥锁,因为有一个“东西”(即内存块)需要同步访问:所有工作线程和推动工作的线程之间的共享状态

对于一个或多个线程需要等待的条件,每个条件变量有一个条件变量。至少你需要一个条件变量来等待新的工作,这里的条件是:“还有更多的事情要做吗?”(或者反过来说:“工作队列是空的吗?”)


一个更实质性的答案是,互斥体和相关条件变量之间存在一对多关系,共享状态和互斥体之间存在一对一关系。根据您告诉我们的以及您正在学习的内容,我建议您在设计中只使用一个共享状态。当或者如果您需要多个状态时,我建议您寻找一些更高级别的概念(例如渠道、未来/承诺)来建立抽象


在任何情况下,不要将同一条件变量用于不同的互斥体。

一个互斥体和至少一个条件变量

一个互斥锁,因为有一个“东西”(即内存块)需要同步访问:所有工作线程和推动工作的线程之间的共享状态

对于一个或多个线程需要等待的条件,每个条件变量有一个条件变量。至少你需要一个条件变量来等待新的工作,这里的条件是:“还有更多的事情要做吗?”(或者反过来说:“工作队列是空的吗?”)


一个更实质性的答案是,互斥体和相关条件变量之间存在一对多关系,共享状态和互斥体之间存在一对一关系。根据您告诉我们的以及您正在学习的内容,我建议您在设计中只使用一个共享状态。当或者如果您需要多个状态时,我建议您寻找一些更高级别的概念(例如渠道、未来/承诺)来建立抽象


无论如何,不要对不同的互斥体使用相同的条件变量。

我认为,如果您将队列组织为堆栈/链表,则可以通过互锁操作从队列中窃取工作,而无需锁定(这将需要信号量而不是条件变量,以防止此答案注释中描述的问题)

伪代码如下所示:

  • 候选人=负责人
  • if(candidate==null)等待信号量
  • 如果(候选者==联锁比较交换(头,候选者->下一步,候选者))执行工作(候选者->数据)
  • 否则转到1
    当然,在这种情况下,向队列中添加工作也应该通过InterlocatedCompareeExchange并向信号量发送信号。

    我认为,如果您将队列组织为堆栈/链表,您可以通过互锁操作从队列中窃取工作而不必锁定(它需要信号量而不是条件变量,以防止此答案注释中描述的问题)

    伪代码如下所示:

  • 候选人=负责人
  • if(candidate==null)等待信号量
  • 如果(候选者==联锁比较交换(头,候选者->下一步,候选者))执行工作(候选者->数据)
  • 否则转到1

  • 当然,在这种情况下,向队列添加工作也应该通过InterconnectedCompareeExchange并向信号量发送信号。

    要详细说明@Ivan的解决方案

    您可以使用计数信号量+原子操作来创建非常高效的队列,而不是互斥+条件变量

    semaphore dequeue_sem = 0;
    semaphore enqueue_sem = 37; // or however large you want to bound your queue
    
    排队操作只是:

    wait_for(enqueue_sem)
    atomic_add_to_queue(element)
    signal(dequeue_sem)
    
    出列操作是:

    wait_for(dequeue_sem)
    element = atomic_remove_from_queue()
    signal(enqueue_sem)
    
    “原子的添加到队列”和“原子的从队列中移除”通常是在一个紧密的循环中使用原子比较和交换来实现的

    除了对称性之外,此公式还限制了队列的最大大小;如果线程在完整队列上调用enqueue(),它将阻塞。这几乎可以肯定是多线程环境中任何队列所需要的。(您的计算机内存有限;应尽可能避免不受限制地使用它。)


    如果您坚持使用互斥锁和条件变量,您需要两个条件,一个用于排队等待(deque发出信号),另一个用于相反的方式。这些条件分别表示“队列未满”和“队列未空”,且排队/出队代码类似对称。

    详细说明@Ivan的解决方案

    您可以使用计数信号量+原子操作来创建非常高效的队列,而不是互斥+条件变量

    semaphore dequeue_sem = 0;
    semaphore enqueue_sem = 37; // or however large you want to bound your queue
    
    排队操作只是:

    wait_for(enqueue_sem)
    atomic_add_to_queue(element)
    signal(dequeue_sem)
    
    出列操作是:

    wait_for(dequeue_sem)
    element = atomic_remove_from_queue()
    signal(enqueue_sem)
    
    “原子的添加到队列”和“原子的从队列中移除”通常是在一个紧密的循环中使用原子比较和交换来实现的

    除了对称性之外,此公式还限制了队列的最大大小;如果线程在完整队列上调用enqueue(),它将阻塞。这几乎可以肯定是多线程环境中任何队列所需要的。(您的计算机内存有限;应尽可能避免不受限制地使用它。)


    如果您坚持使用互斥锁和条件变量,那么您需要两个条件,一个用于排队等待(deque发送信号),另一个用于相反的方式。这些条件分别表示“队列未满”和“队列未空”,且入/出队列代码类似对称。

    +1。您可能还需要另一个条件“队列未满”。无界数据结构+异步=诱人的命运+1。您可能需要“队列未满”的另一个条件