0

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

队列应该受到互斥锁的保护。

但在我的主要我不想做:

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

因此,您需要使用条件变量来向 main 发出信号表明某些事情发生了变化。现在我只能阻塞 1 个条件变量,所以我需要所有这些线程使用相同的条件变量和附带的互斥锁。现在我不想真正使用这个互斥锁来锁定我所有的线程。这并不意味着当一个线程写入队列时,另一个线程不能写入完全不同的队列。所以我为每个队列使用单独的互斥锁。但是现在我如何使用条件变量附带的这个额外的互斥锁。

它是如何使用 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
 };

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

4

3 回答 3

3

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

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

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

我会说从一个队列开始,看看它能让你走多远。然后,当您遇到一个限制时,您确实有许多线程在一个互斥体上等待,您将获得有关该问题的更多信息,因此能够更好地解决它。

从本质上讲,我会说您面临此问题的原因是过早的设计优化,而解决该问题的方法是立即回溯并更改设计。

于 2013-03-21T23:27:37.160 回答
2

创建一个包含所有在其中工作的队列的顶级(可能是循环的)队列。

队列可以由单个互斥体保护,并且具有一个 condvar,仅当它从空变为非空时才需要发出信号。

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

一些细节将取决于您是否希望您的线程依次只从每个非空队列中获取最前面的项目,或者按顺序消耗每个整个队列,但想法就在那里。


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

正如我所说,这取决于你如何消费它们。如果每次队列中有东西时,您都这样做:

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

那么工作队列要么总是非空的,因此在顶级队列中,要么是空的,因此不在队列中。

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


请注意,如果

... 包裹进来相对较慢。而且队列大部分时间都是空的

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

于 2013-03-21T22:04:46.867 回答
0

@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
  }
};
于 2013-03-26T02:50:35.650 回答