c++ std条件变量覆盖了很多共享变量

C++ std condition variable covering a lot of share variables

本文关键字:共享变量 覆盖 std 条件 变量 c++      更新时间:2023-10-16

我正在用c++编写一个多线程程序,在我的主线程中,我正在等待其他线程将包放在不同的队列中。这取决于包的类型和它们来自哪个线程。

队列受到互斥锁的保护。

但我主要不想这样做:

while(true)
if(!queue1->empty)
{
     do stuff
}
if(!queue2->empty)
{
     do stuff
}
etc

所以你需要使用条件变量来通知主程序发生了变化。现在我只能阻塞一个条件变量,所以我需要所有这些线程使用相同的条件变量和伴随的互斥锁。现在我不想用这个互斥锁来锁定所有的线程。这并不意味着当一个线程写入队列时,另一个线程不能写入完全不同的队列。所以我为每个队列使用单独的互斥锁。但是现在我该如何使用这个附加的互斥锁呢?

如何使用boost完成2线程和1队列,非常类似于std。http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html:

template<typename Data>
class concurrent_queue
{
    private:
    boost::condition_variable the_condition_variable;  
    public:
    void wait_for_data()
    {
        boost::mutex::scoped_lock lock(the_mutex);
        while(the_queue.empty())
        {
             the_condition_variable.wait(lock);
        }
    }
    void push(Data const& data)
    {
         boost::mutex::scoped_lock lock(the_mutex);
         bool const was_empty=the_queue.empty();
         the_queue.push(data);
         if(was_empty)
         {
             the_condition_variable.notify_one();
         }
     }
     // rest as before
 };

那么如何解决这个问题呢?

我想说你的问题的关键在这里:

Now I dont want to really use this mutex to lock all my threads. It doesn't mean that when 1 thread is writing to a queue, another cant write to a totally different queue. So I use seperate mutexes for each queue.

为什么?因为:

... packages come in relatively slow. And queues are empty most of the time

在我看来,你把自己设计到一个角落,因为你认为你需要的东西,而实际上你可能并不需要它,因为一个队列实际上可以在你提到的使用场景中工作。

我想说从一个队列开始,看看它能让你走多远。然后,当你遇到一个限制,你真的有很多线程在等待一个互斥锁时,你就会有更多关于这个问题的信息,从而能够更好地解决它。

从本质上讲,我想说你面临这个问题的原因是过早的设计优化,解决这个问题的方法是现在就回过头来改变设计。

为所有有工作的队列创建一个顶级(可能是循环的)队列。

这个队列可以通过一个互斥锁来保护,并且只有当它从空变为非空时才需要发出信号。

现在,您的每个队列都可以有自己的互斥锁,并且只有当从空变为非空时,它们才需要接触共享/顶级队列(及其互斥锁)。

一些细节将取决于你是否希望你的线程只依次从每个非空队列中取出前面的项目,还是顺序地消耗每个队列,但是思想是存在的。


从非空到非空(但大小增加)也应该传递到顶级队列?

正如我所说的,这取决于你如何消费它们。如果,每当队列中有内容时,您可以这样做:

  1. (你已经有了顶级锁,这就是你知道这个队列中有东西的方式)
  2. 锁定队列
  3. 用本地工作副本交换队列内容
  4. 从顶级队列中删除队列
  5. 解锁队列

则工作队列总是非空,因此在顶级队列中,空,因此不在队列中。

如果这样做,而只是从每个非空队列中拉出前面的元素,那么您需要考虑更多的状态。


注意如果

…包裹来的相对较慢。队列大部分时间是空的

您可能只有一个队列,因为没有足够的活动来引起很多争用。这极大地简化了事情。

@Carleeto和@Useless都给出了很好的答案。您只有一个消费者,因此单个队列将为您提供最佳的性能。您无法获得比持续工作的单个消费者更高的吞吐量,因此您的目标应该是最小化单个消费者的锁定开销,而不是生产者。您可以通过让生产者等待单个条件变量(指示队列非空)和单个互斥锁来实现这一点。

下面是如何实现参数多态性的。完全类型安全,不强制转换,父类中只有一个虚函数:
class ParentType {
public:
  virtual void do_work(...[params]...)=0;
  virtual ~ParentType() {}
};
class ChildType1 : public ParentType {
private:
  // all my private variables and functions
public:
  virtual void do_work(...[params]...) {
    // call private functions and use private variables from ChildType1
  }
};
class ChildType2: public ParentType {
private:
  // completely different private variables and functions
public:
  virtual void do-work(...[params]...) {
    // call private functions and use private variables from ChildType2
  }
};