C++11 中是否有并发容器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7817364/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are there any concurrent containers in C++11?
提问by fredoverflow
In particular, I am looking for a blocking queue. Is there such a thing in C++11? If not, what are my other options? I really don't want to go down to the thread level myself anymore. Way too error-prone.
特别是,我正在寻找阻塞队列。C ++ 11中有这样的东西吗?如果没有,我的其他选择是什么?我真的不想再深入到线程级别了。太容易出错了。
采纳答案by Lior Kogan
According to Diego Dagum from Microsoft's Visual C++ Team:
根据微软 Visual C++ 团队的 Diego Dagum 的说法:
A recurrent question (well, one of the many) is about STL containers and whether they are thread safe.
Taking Stephan's words here, the reality is that they aren't, not as a bug but as a feature: having every member function of every STL container acquiring an internal lock would annihilate performance. As a general purpose, highly reusable library, it wouldn't actually provide correctness either: the correct level to place locks is determined by what the program is doing. In that sense, individual member functions don't tend to be such correct level.
一个反复出现的问题(好吧,其中之一)是关于 STL 容器以及它们是否是线程安全的。
用 Stephan 的话来说,事实是它们不是,不是作为一个错误,而是作为一个特性:让每个 STL 容器的每个成员函数都获得一个内部锁会破坏性能。作为一个通用的、高度可重用的库,它实际上也不会提供正确性:放置锁的正确级别取决于程序正在做什么。从这个意义上说,单个成员函数往往不是那么正确的级别。
The Parallel Patterns Library(PPL) includes several containers that provide thread-safe access to their elements:
并行模式库(PPL) 包括几个容器,提供对其元素的线程安全访问:
- The concurrent_vector Classis a sequence container class that allows random access to any element. It enables concurrency-safe append, element access, iterator access and iterator traversal operations.
- The concurrent_queue Classis a sequence container class that allows first-in, first-out access to its elements. It enables a limited set of concurrency-safe operations, such as push and try_pop, to name a few.
- 的concurrent_vector类是一个序列容器类,允许的任何元素的随机接入。它支持并发安全的追加、元素访问、迭代器访问和迭代器遍历操作。
- 所述concurrent_queue类是一个序列容器类,允许先入先出访问它的元件。它启用了一组有限的并发安全操作,例如 push 和 try_pop,仅举几例。
Some samples here.
这里有一些样品。
Also interesting: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html.
同样有趣:http: //www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html。
回答by Lars K.
C++11 does not provide concurrent containers by itself. However, there are library options. Besides the already mentioned PPL, don't forget the Intel TBB library.
C++11 本身不提供并发容器。但是,有库选项。除了已经提到的 PPL,不要忘记 Intel TBB 库。
It has a concurrent queue
, hash_map
, set
and vector
implementation. But it's not only a thread-safe container library, it also comes with parallel version of standard algorithms (for-loop, reduce, sort,...).
它有一个并发的queue
,hash_map
,set
和vector
实施。但它不仅是一个线程安全的容器库,它还带有标准算法的并行版本(for-loop、reduce、sort,...)。
回答by Miljen Mikic
I am surprised that nobody mentioned moodycamel::ConcurrentQueue. We have been using it for quite some time and it performs very well. It is specific that its implementation is lock-free, which immediately brings an enormous speed. Other reasons for using it (quoting from the official site):
我很惊讶没有人提到moodycamel::ConcurrentQueue。我们已经使用它一段时间了,它的表现非常好。特别是它的实现是无锁的,这立即带来了巨大的速度。其他使用原因(引自官网):
There are not that many full-fledged lock-free queues for C++. Boost has one, but it's limited to objects with trivial assignment operators and trivial destructors, for example. Intel's TBB queue isn't lock-free, and requires trivial constructors too. There's many academic papers that implement lock-free queues in C++, but usable source code is hard to find, and tests even more so.
C++ 没有那么多成熟的无锁队列。Boost 有一个,但它仅限于具有普通赋值运算符和普通析构函数的对象,例如。Intel 的 TBB 队列不是无锁的,也需要简单的构造函数。有很多学术论文在 C++ 中实现了无锁队列,但是很难找到可用的源代码,而且测试更是如此。
Some benchmarks and comparisons are available here, hereand here.
回答by justin
The containers' interfaces have simply not been designed with this objective. For the interfaces they use, a lock visible to the client is really the only way you could accomplish this while guaranteeing correctness and predictable behaviour. It would also be terribly inefficient because the number of acquisitions would be very high (relative to a good implementation).
容器的接口根本就不是为了这个目的而设计的。对于他们使用的接口,客户端可见的锁实际上是您在保证正确性和可预测行为的同时实现这一目标的唯一方法。这也会非常低效,因为收购的数量会非常多(相对于良好的实施而言)。
Solution 1
解决方案1
Pass by value (where applicable).
按值传递(如果适用)。
Solution 2
解决方案2
Create a collection of simple bolt-on implementations that you can use to pass containers while holding a scope lock (consider it pseudo c++):
创建一个简单的附加实现的集合,您可以使用它们在持有范围锁的同时传递容器(将其视为伪 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;
};
then the caller pairs the lock with the collection, and then you update your interfaces over to use (pass by) the container type where appropriate. It's just a poor man's class extension.
然后调用者将锁与集合配对,然后更新您的接口以在适当的情况下使用(传递)容器类型。这只是一个穷人的阶级延伸。
This locked container is one simple example, and there are a few other variants. This is the route I chose because it really allows you to use the granularity level which is ideal for your program, even though it not as transparent (syntactically) as locked methods. It's also relatively easy to adapt existing programs. At least it behaves in a predictable manner, unlike collections with internal locks.
这个上锁的容器是一个简单的例子,还有一些其他的变体。这是我选择的路线,因为它确实允许您使用适合您的程序的粒度级别,即使它不像锁定方法那样透明(在语法上)。适应现有程序也相对容易。至少它以可预测的方式运行,这与具有内部锁的集合不同。
Another variant would be:
另一种变体是:
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;
...where a type similar to t_locked_collection
could be used to expose the underlying collection. Not to imply that approach is foolproof, just fool resistant.
...t_locked_collection
可以使用类似于 的类型来公开底层集合。并不是暗示这种方法是万无一失的,只是抗拒。
回答by Asif Bahrainwala
My version of a concurrent undordered map namespace concurrency {
我的并发无序映射命名空间并发{
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);
}
};
}
回答by gm127
There are no concurrent containers in C++11.
C++11 中没有并发容器。
But the following header class provides concurrent queue, stack and priority containers using std::deque.
但是以下头类使用 std::deque 提供并发队列、堆栈和优先级容器。
BlockingCollectionis a C++11 thread safe collection class that is modeled after the .NET BlockingCollection class.
BlockingCollection是一个 C++11 线程安全集合类,它模仿 .NET BlockingCollection 类。