Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有细粒度锁的线程安全链表_C++_Linux_Multithreading_Boost_Linked List - Fatal编程技术网

C++ 具有细粒度锁的线程安全链表

C++ 具有细粒度锁的线程安全链表,c++,linux,multithreading,boost,linked-list,C++,Linux,Multithreading,Boost,Linked List,在一个程序中,我有一个M类: class M{ /* very big immutable fields */ int status; }; 我需要一个M型物体的链表 有三种类型的线程正在访问列表: 生产者:产生对象并将其附加到列表的末尾。所有新生成的对象都具有状态=新建。(操作时间=O(1)) 使用者:使用列表开头的对象。如果某个对象的状态为consumer\u ID,则该对象可以被消费者消费。每个消费者都保留链接列表中其可以消费的第一个项目,因此该消费

在一个程序中,我有一个M类:

class M{
    /*
      very big immutable fields
    */
    int status;
};
我需要一个M型物体的链表

有三种类型的线程正在访问列表:

  • 生产者:产生对象并将其附加到列表的末尾。所有新生成的对象都具有状态=新建。(操作时间=O(1))
  • 使用者:使用列表开头的对象。如果某个对象的状态为consumer\u ID,则该对象可以被消费者消费。每个消费者都保留链接列表中其可以消费的第一个项目,因此该消费为(摊销?)O(1)(参见下面的注释)
  • 析构函数:当有通知说对象已正确使用时(操作时间=O(1))删除已使用的对象
  • 修改器:根据状态图更改对象的状态。任何对象的最终状态都是使用者的id(每个对象的操作时间=O(1))
消费者人数不到10人。生产商的数量可能多达数百家。有一个修饰符

注意:修改器可能会修改已消费的对象,因此消费者的存储项可能会前后移动。对于这个问题,我没有找到更好的解决方案(尽管对象之间的比较是O(1),操作不再是摊销O(1))

演出很重要。因此,我希望使用原子操作或细粒度锁(每个对象一个)来避免不必要的阻塞

我的问题是:

  • 原子操作是首选,因为它们更轻。我想我必须使用锁来更新析构函数线程中的指针,并且我可以使用原子操作来处理其他线程之间的争用。请让我知道,如果我遗漏了什么,并且有一个原因,我不能在状态字段上使用原子操作

  • 我想我不能使用STL列表,因为它不支持细粒度锁。但是你会推荐使用Boost::侵入式列表(而不是编写我自己的列表)吗?有人提到,侵入式数据结构更难保证线程安全?对于细粒度锁,这是真的吗

  • 生产者、消费者和析构函数将根据某些事件异步调用(我计划使用Boost::asio。但我不知道如何运行修饰符以最小化与其他线程的争用。选项有:

    • 从生产者异步
    • 从使用者异步发送
    • 使用自己的计时器
  • 任何这样的调用只有在某些条件成立的情况下才会在列表上运行。我自己的直觉是,我调用修饰符的方式没有区别。我是否遗漏了什么

    我的系统是Linux/GCC,我使用Boost1.47以防万一

    类似问题:

    性能非常重要。因此,我想使用原子操作或细粒度锁(每个对象一个)来避免不必要的阻塞

    这会增加争用(访问相同数据)的线程在不同内核上同时运行的可能性,从而使性能更差。如果锁太细,线程可能争用(在缓存之间乒乓数据)并以缓慢的锁步运行,而不会阻塞锁,从而导致糟糕的性能

    您希望使用足够粗糙的锁,以使争用同一数据的线程尽快相互阻塞。这将迫使调度程序调度非争用线程,从而消除破坏性能的缓存乒乓


    您有一个常见的错误观念,认为阻塞是不好的。事实上,争用是不好的,因为它会将内核速度降低到总线速度。阻塞结束争用。阻塞是好的,因为它会取消争用线程的调度,允许非争用线程(可以全速并发运行)正如@David Schwartz所指出的,阻塞并不总是很慢,而且(在用户空间多线程应用程序中)旋转可能非常危险

    此外,linux pthread库具有pthread_互斥体的“智能”实现。它被设计为“轻量级”,即当一个线程试图锁定已经获得的互斥体时,它会旋转一段时间,在阻塞之前多次尝试获取锁。尝试次数不足以损害您的系统,甚至不符合实时要求(如果有的话)。另外一个linux特有的特性是所谓的(FUTEX),它减少了系统调用的数量。其主要思想是,只有当线程确实需要阻塞互斥时,它才会执行互斥锁系统调用(当线程锁定未获取的互斥锁时,它不会执行系统调用)

    事实上,在大多数情况下,你不需要重新发明轮子或引入一些非常具体的锁定技术。如果必须的话,那么要么是设计出了问题,要么是处理的是高度并发的环境(乍一看,10个消费者似乎不是这样,而且所有这些看起来都像是过度工程)

    • 如果我是你,我更喜欢使用条件变量+互斥来保护列表
    • 我要做的另一件事是再次检查设计。当消费者需要搜索列表是否包含ID为的项目时,为什么要使用一个全局列表(如果是,请将其删除/出列)?可能最好为每个消费者创建一个单独的列表?在这种情况下,您可能可以删除状态字段
    • 读访问比写访问更频繁吗?如果是这样,最好使用R/W锁或
    • 如果我对pthread原语和futex之类的东西不满意(如果我不满意,我会通过测试证明锁定原语是瓶颈,而不是消费者的数量或我选择的算法),然后我会尝试考虑复杂的算法,包括引用计数、单独的GC线程和所有更新的原子限制

    如果您已经计划使用Boost Asio,那么好消息是!您可以停止编写自定义Asio
    boost::asio::io_service io_service_;
    
    void produce()
    {
        M* m = new M;
        io_service_.post(boost::bind(&consume, m));
    }
    
    void consume(M* m)
    {
        delete m;
    }
    
    producer()
    {
      while (true)
      {
        o = get_object_from_somewhere ()
        atomic_enqueue (SQ.queue, o)
        signal(SQ.sem)
      }
    }
    
    consumer()
    {
      while (true)
      {
        wait (CQ[self].sem)
        o = atomic_dequeue (CQ[self].queue)
        process (o)
        destroy (o)
      }
    }
    
    modifier()
    {
      while (true)
      {
        wait (SQ.sem)
        o = atomic_dequeue (SQ.queue)
        FSM (o)
        atomic_enqueue (CQ [o.status].queue, o)
        signal (CQ [o.status].sem)
      }
    }