【问题标题】:C++ std condition variable covering a lot of share variablesC++ std条件变量覆盖了很多共享变量
【发布时间】:2013-03-11 15:13:28
【问题描述】:

我正在用 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
 };

那么你是怎么解决这个问题的呢?

【问题讨论】:

    标签: c++ multithreading variables conditional


    【解决方案1】:

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

    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

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

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

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

    【讨论】:

    • 一个队列的问题是包是不同类的对象。根据它们起源于不同线程的类型,并且还需要在主线程中进行不同的处理。 main 还制作包并将它们发送到线程。我正在通过串行端口与 zigbee 通信。我有一个正在运行的 web 服务,它接受 HTTP 包,我是一个 web 服务的客户端,它运行某种外来的数据库,我在其中放置数据。所以 1 个队列不会工作导致来自不同类的数据包或对象。而且我不想一直在铸造它们。 (或者我应该?)
    • 您可以将类型编码为数字字段,这样可以消除转换问题。
    • Idd 我可以这样做,但这样做我感觉不太舒服。如果没有必要,一般的经验法则不会太投。但我想在这种情况下它可能是必要的。
    • 你没有为了将不同的类型放在同一个队列中而进行强制转换——在某些情况下你可以只使用运行时多态性。典型的例子是知道如何“运行”自己的Task 子类,或访问者模式。
    • 问题是子类有不同的功能,而父类不存在。在父级中将所有功能设为纯虚拟是没有意义的,因为有些子级根本不支持它们。 +您将在父级中获得大量虚拟功能列表。所以仅仅多态性是不够的。我将编写一个包含包队列和互斥锁以及包的类型标识符的类,以便我可以将它们转换回原始包。所有的转换都将在课堂内完成,我将为每种类型的数据包制作不同的 setter 和 getter。谢谢!
    【解决方案2】:

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

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

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

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


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

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

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

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

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


    注意如果

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

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

    【讨论】:

    • 好包进来相对较慢。而且队列大部分时间都是空的。因此,每次有包裹进来时,我仍然需要访问顶级队列。此外,我不明白为什么仅当单个队列从空变为非空时才需要锁定顶级队列。从非空到非空(但增加了大小)也应该传递到顶级队列?我想我可能会将共享队列中的项目复制到我主要的本地队列中。因为处理可以发生一些地方。所以清空队列会相对较快。只需 queue.front() 和 queue.pop()。
    • 锁定所有队列不会让我的程序变得那么糟糕,但它是论文的一部分,我想尽可能地优化它。我可以只使用一个锁定并用于我的条件变量但不锁定任何队列的互斥锁。还是您认为这会在某处造成问题?
    【解决方案3】:

    @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
      }
    };
    

    【讨论】:

      猜你喜欢
      • 2017-02-25
      • 2019-04-23
      • 2012-04-29
      • 2020-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-11
      相关资源
      最近更新 更多