首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >这个多线程用例的最佳数据结构:入侵列表好吗?

这个多线程用例的最佳数据结构:入侵列表好吗?
EN

Stack Overflow用户
提问于 2009-05-30 02:30:12
回答 2查看 1.8K关注 0票数 2

我需要设计一个支持以下操作的数据结构:

  • 搜索元素在数据结构中基于一个键即为间隔。例如,间隔1-5内的值可以是3,从6-11可以是5,依此类推。间隔是连续的,它们之间没有重叠。查找前间隔和下一个间隔--这几乎和搜索interval.
  • split的间隔一样频繁,连接连续的intervals
  • concurrency:--我已经将设计限制在一个写入线程和其他读取器线程上,如下所示。写入线程可以拆分或连接间隔,也可以在间隔中修改值。任何读取器线程只在一个时间间隔内读取值(读取器可以读取多个时间间隔,但是我不必序列化所有读取--在两个读取之间写入是可以的)。每个读者每写大约有20-80个读物.此外,我仍然需要决定读者的数量,但它将在2-8左右。

我考虑使用list添加和删除中间的元素。只有有限的间隔--所以使用地图可能是不对的。这种访问(一个作者,多个读者)不受STL列表的良好支持.boost::侵扰性::列表似乎是合适的。在入侵列表的顶部,我将不得不获得锁来读取/写入间隔。

此外,我了解到,与STL列表相比,入侵列表可能用于更好的缓存局部性(以及对包含的对象进行适当的内存分配)。

这个方法还好吗?如果是,我还想了解您在使用侵入性::list方面的经验,特别是对于多线程应用程序。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2009-05-30 13:25:36

这里有两个不同的问题:

  1. 如何表示您的数据结构
  2. 如何在高效的庄园

中使其线程安全

您的数据结构将对每次写入进行(20-80) x (2-8)读取。

(1)。首先,假设您的范围是一个数据结构,如下所示

代码语言:javascript
运行
复制
    struct Interval
    {
        Interval(int start, int length)
          : m_start(start),
            m_length(length)
        {}
        int m_start;
        int m_length;
        int value; // Or whatever
    };

Since reads massively outnumber writes, lookup needs to be fast, while modifications don't.

Using a list for your data structure means O(N) lookups and O(1) modification - exactly the wrong way around.

The simplest possible representation of your structure is a vector. If the intervals are held in sorted order, lookups are O(logN) and modifications are O(N).

To implement this, just add a comparator to Interval:

代码语言:javascript
运行
复制
bool operator<(const Interval& rhs) const
{
    return m_start < rhs.m_start;
}

You can then use std::lower_bound to find the first interval equal or lower to your search interval in O(logN).

Next and previous interval are O(1) - decrement or increment the returned iterator.

Splitting an interval means inserting a new element after the current one and adjusting the length of the current - O(N).

Joining two intervals means adding the length of the next one to the current one and erasing the next one - O(N).

You should reserve() enough space in the vector for the maximum number of elements to minimise resizing overhead.

(2). Following Knuth, 'premature optimisation is the root of all evil'.

A single read/write lock on the structure holding your vector<Interval>很可能就足够了。唯一可能的问题是(2a),因为读取器垄断了锁,或者(2b)读取器饥饿,因为作者更新需要太长时间。

(2a)如果(并且只有在)你面临作家饥饿的情况下,你可以使锁定变得更细。但极有可能情况并非如此。为此,请执行以下操作:

通过指针而不是值使向量保持其间隔。这样,调整大小就不会在内存中移动对象。让每个间隔包含一个读/写锁。

对于读:获取集合的读锁,然后是您想要的间隔。如果不需要读取任何其他时间间隔,则在获得间隔锁后立即放弃收集锁,以允许其他线程继续执行。

如果您需要读取其他桶,您可以按任何顺序读取-锁定它们,直到您放弃集合读锁,此时写入器可能会添加或删除任何未锁定的时间间隔。在获取这些锁时,顺序并不重要,因为当您在集合上持有一个读锁时,写入器不能更改向量,并且读锁不会争用。

写出来:

获取集合的写锁,然后是您想要的间隔。请注意,必须保持集合写锁的所有更新,将添加或删除间隔。如果只更新一个间隔,则可以放弃集合锁。否则,您需要持有写锁,并在您将要修改的任何时间间隔内获取一个写锁。您可以按任何顺序获取间隔锁,因为没有集合锁,任何读取器都无法获得新的读锁。

以上的工作是通过对作家的线更自私,这应该消除饥饿。

(2b)如果您面临读者饥饿,这是更不可能的,最好的解决方案是将集合写入和读取分开。按共享指针保存集合,并在其上设置一个写锁。

用于读取:获取写入锁和shared_ptr的副本。放弃写锁。读取器现在可以在没有任何锁(其不可变)的情况下读取集合。

用于写入:按照读取器将shared_ptr带到集合中,放弃锁。创建集合的私有副本并对其进行修改(因为它是私有副本,因此不需要锁)。再次使用写锁,用新集合替换现有的shared_ptr。使用旧集合完成的最后一个线程将销毁它。所有未来的线程都将使用新更新的集合。

注意,根据您的问题描述,此算法只适用于一个编写器。

票数 5
EN

Stack Overflow用户

发布于 2009-05-30 02:38:04

并发二叉树可能是一个很好的适合,允许读取和写入不同的间隔并行进行。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/928827

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档