跟踪多线程中的数据引用(多少/谁)

时间:2012-01-10 08:17:10

标签: c++ multithreading mutex context-switch

我在多线程中遇到了一个问题,多线程模型是1生产者 - N消费者。

生产者生成数据(每个字符数据大约200字节),将其放入固定大小的缓存(即2Mil)。数据与所有线程无关。它应用过滤器(已配置)并确定没有符合生成数据的线程。

生产者将指向数据的指针推送到合格线程的队列中(仅指向数据的指针以避免数据复制)。线程将deque并通过TCP / IP发送给客户端。

问题:由于只有指向数据的指针被赋予多个线程,当缓存变满时,Produces想要删除第一个项目(旧的)。任何线程仍然可以引用数据的可能性。

可行的方法:使用Atomic粒度,当生产者确定合格线程的数量时,它可以更新计数器和线程ID列表。

class InUseCounter
{
    int           m_count;
    set<thread_t> m_in_use_threads;
    Mutex         m_mutex;
    Condition     m_cond;

public:
    // This constructor used by Producer
    InUseCounter(int count, set<thread_t> tlist)
    {
        m_count          = count;
        m_in_use_threads = tlist;
    } 

    // This function is called by each threads
    // When they are done with the data, 
    // Informing that I no longer use the reference to the data.
    void decrement(thread_t tid)
    {
        Gaurd<Mutex> lock(m_mutex);
        --m_count;
        m_in_use_threads.erease(tid);
    }

    int get_count() const { return m_count; }
};

掌握chache

map<seqnum, Data>
              |
              v
             pair<CharData, InUseCounter>

当生产者移除它检查计数器的元素时,它大于0,它发送动作以释放对m_in_use_threads集中线程的引用。

问题

  1. 如果主缓存中有2Mil记录,则会有相同的记录 InUseCounter的数量,因此Mutex变量,这是可取的,在一个单一过程中有2Mil互斥量变量。
  2. 拥有大型单一数据结构来维护InUseCounter 导致更多的锁定时间来查找和减少
  3. 找出参考文献的方法最好的替代方法是什么,以及谁 所有引用都具有非常少的锁定时间。
  4. 感谢您的建议。

3 个答案:

答案 0 :(得分:4)

  1. 200万互斥量有点多。即使它们是轻质锁, 他们仍然需要一些开销。
  2. InUseCounter放在一个结构中会导致线程在释放记录时发生争用;如果线程没有以锁步方式执行,这可能是微不足道的。如果他们经常发布记录并且争用率上升,这显然是性能下降。
  3. 您可以通过让一个线程负责维护记录引用计数(生产者线程)并让其他线程通过单独的队列发回记录释放事件来提高性能,实际上,将生产者转变为记录发布事件使用者。当您需要刷新条目时,首先处理所有发布队列,然后运行发布逻辑。您将有一些延迟来处理,因为您现在正在排队发布事件而不是尝试立即处理它们,但性能应该会好得多。
  4. 顺便提一下,这与Disruptor框架的工作方式类似。它是用于高频交易的高性能Java(!)并发框架。是的,我确实在同一句话中说过高性能的Java和并发性。对高性能并发设计和实现有很多宝贵的见解。

答案 1 :(得分:1)

由于您已经拥有Producer->Consumer队列,因此一个非常简单的系统包含一个“反馈”队列(Consumer->Producer)。

在使用了一个项目后,消费者将指针反馈给Producer,以便Producer可以删除该项目并更新缓存的“空闲列表”。

这样,只有Producer会触及缓存内部,并且不需要同步:只需要同步队列。

答案 2 :(得分:0)

  1. 是的,2000000互斥量是一种过度杀伤力。
  2. 一个大的结构将被锁定更长时间,但需要更少的锁定/解锁。
  3. 最好的方法是使用shared_ptr智能指针:它们似乎是为此量身定制的。你不自己检查柜台,你只需清理你的指针。 shared_ptr是线程安全的,而不是它指向的数据,但是对于1个生产者(编写者)/ N个消费者(读者),这应该不是问题。