C++11 中是否有任何并发容器?[已结束]

Are there any concurrent containers in C++11? [closed]

提问人:fredoverflow 提问时间:10/19/2011 更新时间:12/30/2020 访问量:48780

问:


我们不允许向读者、工具、软件库等寻求推荐的问题。您可以编辑问题,以便用事实和引文来回答。

5年前关闭。

特别是,我正在寻找一个阻塞队列。C++11中有这样的东西吗?如果没有,我还有什么其他选择?我真的不想再自己下到线程级别了。太容易出错了。

C++ visual-studio-2010 并发 C++11 阻塞队列

评论

3赞 Alok Save 10/19/2011
+1,有趣的Q.Scott Meyers在C++x天问这个问题。知道这在 C++11 之后是如何变化的会很有趣。
0赞 David Heffernan 10/19/2011
使用原语将标准队列转换为阻塞队列非常容易

答:

43赞 Lior Kogan 10/19/2011 #1

根据 Microsoft Visual C++ 团队的 Diego Dagum 的说法

一个反复出现的问题(嗯,众多问题之一)是关于 STL 容器的 以及它们是否安全。

用斯蒂芬的话来说,现实情况是他们不是,不是 错误但作为一个功能:每个 STL 的每个成员都具有功能 容器获取内部锁会破坏性能。如 一个通用的、高度可重用的库,它实际上不会 提供正确性:放置锁的正确级别是 由程序正在执行的操作决定。从这个意义上说,个人 成员函数往往不是这样的正确级别。

并行模式库 (PPL) 包括多个容器,这些容器提供对其元素的线程安全访问:

  • concurrent_vector类是一个序列容器类,允许随机访问任何元素。它支持并发安全追加、元素访问、迭代器访问和迭代器遍历操作。
  • concurrent_queue类是一个序列容器类,允许先进先出访问其元素。它支持一组有限的并发安全操作,例如推送和try_pop,仅举几例。

这里有一些示例。

同样有趣的是:http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

评论

3赞 Andrey Suvorov 12/2/2016
是的。但问题是 - 它仅适用于 Windows =(
0赞 Joel 1/6/2021
我不认为concurrent_queue类是严格意义上的FIFO:“在这里,并发安全意味着指针或迭代器始终有效。它不能保证元素初始化,也不能保证特定的遍历顺序。 learn.microsoft.com/en-us/cpp/parallel/concrt/......
1赞 justin 10/19/2011 #2

容器的接口根本没有为此目标而设计。对于他们使用的接口,客户端可见的锁实际上是在保证正确性和可预测行为的同时实现此目的的唯一方法。这也将非常低效,因为收购的数量将非常高(相对于良好的实施)。

解决方案 1

按值传递(如果适用)。

解决方案 2

创建一个简单的附加实现集合,可用于在保持作用域锁的同时传递容器(将其视为伪 c++):

template <typename TCollection>
class t_locked_collection {
public:
    t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() {
    }

    TCollection& collection;
    // your convenience stuff
private:
    t_scope_lock d_lock;
    t_nocopy d_nocopy;
};

然后,调用方将锁与集合配对,然后更新接口以在适当的情况下使用(传递)容器类型。这只是一个穷人的阶级延伸。

这个锁定的容器是一个简单的例子,还有一些其他的变体。这是我选择的路由,因为它确实允许您使用最适合您的程序的粒度级别,即使它不像锁定方法那样透明(语法上)。调整现有程序也相对容易。至少它的行为方式是可预测的,这与具有内部锁的集合不同。

另一种变体是:

template <typename TCollection>
class t_lockable_collection {
public:
// ...
private:
    TCollection d_collection;
    t_mutex d_mutex;
};

// example:
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector;

...其中,类似于 的类型可用于公开基础集合。并不是说这种方法是万无一失的,只是防万无一失。t_locked_collection

评论

0赞 steffen 5/20/2012
使用“按值传递”,您的意思是按值传递整个容器以创建副本并处理副本?还是按值传递容器的物品?请详细说明。
0赞 justin 5/20/2012
@steffen按值传递容器的元素。考虑到许多容器采用的接口,这(解决方案 1)远非最佳解决方案。这种方法也几乎毫无用处,除非你愿意编写大量的异常处理并使用大量的共享指针。
15赞 user1539190 11/28/2012 #3

C++ 11 本身不提供并发容器。但是,有库选项。 除了已经提到的 PPL 之外,别忘了英特尔 TBB 库。

它有一个并发的 、 和 实现。但它不仅是一个线程安全的容器库,它还带有标准算法的并行版本(for-loop、reduce、sort,...)。queuehash_mapsetvector

英特尔 TBB 网站

评论

1赞 user 6/9/2013
你能给我并发集的链接吗?
10赞 Miljen Mikic 10/6/2016 #4

我很惊讶没有人提到moodycamel::ConcurrentQueue。我们已经使用它很长一段时间了,它的表现非常好。具体来说,它的实现是无锁的,这立即带来了巨大的速度。使用的其他原因(引自官方网站):

C++没有那么多成熟的无锁队列。提高 有一个,但它仅限于具有简单赋值运算符的对象 例如,以及微不足道的析构函数。英特尔的 TBB 队列不是 无锁,也需要简单的构造函数。有很多 在 C++ 中实现无锁队列但可用的学术论文 源代码很难找到,测试更是如此。

一些基准和比较可在此处、此处和此处获得。

注意:在多个生产者的情况下,弹出元素的顺序不能保证与推送元素的顺序 (@IgorLevicki) 相同,因此如果您需要此保证,请寻找其他选项。

评论

0赞 Igor Levicki 8/31/2017
moodycamel 实现的问题在于它不是 FIFO(即 popped 元素的顺序不能保证与推送元素的顺序相同),因此它不是一个通用的解决方案。
0赞 Miljen Mikic 6/30/2020
@IgorLevicki 是的,你是对的。提供的唯一保证是来自同一生产商的元素将保持其相对顺序。
1赞 Asif Bahrainwala 6/25/2018 #5

我的并发无序映射版本 命名空间并发 {

template<typename T,typename T1>
class unordered_bucket: private std::unordered_map<T,T1>
{
mutable std::recursive_mutex m_mutex;

public:
T1 &operator [](T a)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return std::unordered_map<T,T1>::operator [](a);
}

size_t size() const noexcept {
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return  std::unordered_map<T,T1>::size();
}

vector<pair<T,T1>> toVector() const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);

    vector<pair<T,T1>> ret;
    for(const pair<T,T1> &p:*this)
    {
        ret.push_back(p);
    }
    return ret;
}

bool find(const T &t) const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    if(this->std::unordered_map<T,T1>::find(t) == this->end())
        return false;  //not found
    return true;
}
void erase()
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(this->begin(),this->end());
}
void erase(const T &t)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(t);
}
};

#define BUCKETCOUNT 10
template<typename T,typename T1>
class ConcurrentMap
{
std::vector<unordered_bucket<T,T1>> m_v;
public:
ConcurrentMap():m_v(BUCKETCOUNT){}   //using 10 buckets

T1 &operator [](T a)
{
    std::hash<T> h;
    return m_v[h(a)%BUCKETCOUNT][a];
}

size_t size() const noexcept {
    size_t cnt=0;

    for(const unordered_bucket<T,T1> &ub:m_v)
        cnt=cnt+ub.size();

    return  cnt;
}

vector<pair<T,T1>> toVector() const
{
    vector<pair<T,T1>> ret;
    for(const unordered_bucket<T,T1> &u:m_v)
    {
        const vector<pair<T,T1>> &data=u.toVector();
        ret.insert(ret.end(),data.begin(),data.end());
    }
    return ret;
}

bool find(const T &t) const
{
    for(const unordered_bucket<T,T1> &u:m_v)
        if(true == u.find(t))
            return true;
    return false;
}
void erase()
{
    for(unordered_bucket<T,T1> &u:m_v)
        u.erase();
}
void erase(const T &t)
{
    std::hash<T> h;
    unordered_bucket<T,T1> &ub = m_v[h(t)%BUCKETCOUNT];
    ub.erase(t);
}
};
}
1赞 gm127 10/8/2018 #6

C++ 11 中没有并发容器。

但以下头类使用 std::d eque 提供并发队列、堆栈和优先级容器。

BlockingCollection 是一个 C++ 线程安全集合类,它以 .NET BlockingCollection 类为模型。