对于C ++中的无锁队列,我已经进行了大量的搜索。我找到了一些代码和一些试验-但是没有什么我能编译的。无锁哈希也将是受欢迎的。
摘要:到目前为止,我还没有肯定的答案。没有“生产就绪”库,令人惊讶的是,现有的库都不符合STL容器的API。
对于C ++中的无锁队列,我已经进行了大量的搜索。我找到了一些代码和一些试验-但是没有什么我能编译的。无锁哈希也将是受欢迎的。
摘要:到目前为止,我还没有肯定的答案。没有“生产就绪”库,令人惊讶的是,现有的库都不符合STL容器的API。
Answers:
我写了一个无锁队列。它具有功能™:
它可以在GitHub上以简化的BSD许可使用(可以随意分发!)。
注意事项:
Facebook的Folly似乎具有基于C ++ 11的无锁数据结构<atomic>
:
ProducerConsumerQueue与文档和示例代码在这里。
AtomicHashMap与文档和示例代码这里
我敢说这些是目前在生产中使用的,所以我猜它们可以安全地用于其他项目。
干杯!
在检查了大多数给出的答案之后,我只能声明:
答案是否定的。
没有这样的事情可以直接使用。
boost.lockfree尝试创建无锁堆栈和fifo类的c ++实现。
我知道的最接近的东西是Windows互锁单链接列表。当然,仅Windows。
如果您具有多生产者/单消费者队列/ FIFO,则可以使用SLIST或琐碎的Free Lock LIFO堆栈轻松地制作一个LockFree。您要做的是为使用者使用第二个“私有”堆栈(为简便起见,也可以将其作为SLIST进行操作,也可以选择其他任何堆栈模型)。消费者从私有堆栈中弹出项目。每当私有LIFO耗尽时,您都执行Flush而不是弹出共享的并发SLIST(抓取整个SLIST链),然后遍历Flushed列表以将项目按顺序推入私有堆栈。
适用于单生产者/单消费者和多生产者/单消费者。
但是,它不适用于多消费者的情况(单生产者或多生产者)。
而且,就哈希表而言,它们是“条带化”的理想候选者,“条带化”只是将哈希表划分为每个缓存段具有锁定的段。Java并发库就是这样做的(使用32条)。如果您拥有轻巧的读写器锁,则可以同时访问哈希表以进行同时读取,并且只有在有争议的条带上进行写操作(并且可能允许您增加哈希表)时,您才会停止。
如果自己动手,请确保将您的锁与哈希条目交织在一起,而不是将所有锁放在一个相邻的数组中,这样就不太可能出现错误共享。
我可能会晚一点。
解决方案的缺乏(被问到这个问题)主要是由于C ++中的一个重要问题(在C ++ 0x / 11之前):C ++没有并发内存模型。
现在,使用std :: atomic,您可以控制内存排序问题,并进行适当的比较和交换操作。我已经为自己编写了使用C ++ 11和Micheal的危害指标(IEEE TPDS 2004)来实现Micheal&Scott的无锁队列(PODC96)的实现,以避免早期的释放和ABA问题。它工作正常,但是实现起来又快又脏,我对实际性能不满意。代码在bitbucket上可用:LockFreeExperiment
也可以使用双字CAS在没有危险指针的情况下实现无锁队列(但是64位版本只能在使用cmpxchg16b的x86-64上实现),我在此发表了一篇博客文章(队列的未经测试的代码) :为x86 / x86-64实现通用双字比较和交换(LSE博客。)
我自己的基准向我显示,双锁队列(同样在Micheal&Scott 1996年的论文中)的性能与无锁队列一样好(我还没有达到足够的争论,以至于锁数据结构存在性能问题,但是我的板凳对于现在)和Intel TBB的并发队列似乎更好(快了两倍)(相对于FreeBSD 9下的操作系统,这是我到目前为止发现的最低限度,具体取决于操作系统,该数目是8个线程)。 i7具有4个ht核心,因此具有8个逻辑CPU)的线程,并且具有非常奇怪的行为(我的简单基准测试的执行时间从几秒变为几小时!)
遵循STL样式的无锁队列的另一个限制:在无锁队列上使用迭代器没有任何意义。
然后英特尔线程构建模块问世了。一时间,这很好。
PS:您正在寻找并发队列和并发哈希表
以下摘自Herb Sutter的关于并发无锁队列的文章http://www.drdobbs.com/parallel/writing-a-generalized-concurrent-queue/211601363?pgno=1。我进行了一些更改,例如编译器重新排序。人们需要GCC v4.4 +来编译此代码。
#include <atomic>
#include <iostream>
using namespace std;
//compile with g++ setting -std=c++0x
#define CACHE_LINE_SIZE 64
template <typename T>
struct LowLockQueue {
private:
struct Node {
Node( T* val ) : value(val), next(nullptr) { }
T* value;
atomic<Node*> next;
char pad[CACHE_LINE_SIZE - sizeof(T*)- sizeof(atomic<Node*>)];
};
char pad0[CACHE_LINE_SIZE];
// for one consumer at a time
Node* first;
char pad1[CACHE_LINE_SIZE
- sizeof(Node*)];
// shared among consumers
atomic<bool> consumerLock;
char pad2[CACHE_LINE_SIZE
- sizeof(atomic<bool>)];
// for one producer at a time
Node* last;
char pad3[CACHE_LINE_SIZE
- sizeof(Node*)];
// shared among producers
atomic<bool> producerLock;
char pad4[CACHE_LINE_SIZE
- sizeof(atomic<bool>)];
public:
LowLockQueue() {
first = last = new Node( nullptr );
producerLock = consumerLock = false;
}
~LowLockQueue() {
while( first != nullptr ) { // release the list
Node* tmp = first;
first = tmp->next;
delete tmp->value; // no-op if null
delete tmp;
}
}
void Produce( const T& t ) {
Node* tmp = new Node( new T(t) );
asm volatile("" ::: "memory"); // prevent compiler reordering
while( producerLock.exchange(true) )
{ } // acquire exclusivity
last->next = tmp; // publish to consumers
last = tmp; // swing last forward
producerLock = false; // release exclusivity
}
bool Consume( T& result ) {
while( consumerLock.exchange(true) )
{ } // acquire exclusivity
Node* theFirst = first;
Node* theNext = first-> next;
if( theNext != nullptr ) { // if queue is nonempty
T* val = theNext->value; // take it out
asm volatile("" ::: "memory"); // prevent compiler reordering
theNext->value = nullptr; // of the Node
first = theNext; // swing first forward
consumerLock = false; // release exclusivity
result = *val; // now copy it back
delete val; // clean up the value
delete theFirst; // and the old dummy
return true; // and report success
}
consumerLock = false; // release exclusivity
return false; // report queue was empty
}
};
int main(int argc, char* argv[])
{
//Instead of this Mambo Jambo one can use pthreads in Linux to test comprehensively
LowLockQueue<int> Q;
Q.Produce(2);
Q.Produce(6);
int a;
Q.Consume(a);
cout<< a << endl;
Q.Consume(a);
cout<< a << endl;
return 0;
}
我发现了用c编写的另一种解决方案:
我写这篇文章的时间大概是在2010年,我相信在不同参考文献的帮助下。多生产者单一消费者。
template <typename T>
class MPSCLockFreeQueue
{
private:
struct Node
{
Node( T val ) : value(val), next(NULL) { }
T value;
Node* next;
};
Node * Head;
__declspec(align(4)) Node * InsertionPoint; //__declspec(align(4)) forces 32bit alignment this must be changed for 64bit when appropriate.
public:
MPSCLockFreeQueue()
{
InsertionPoint = new Node( T() );
Head = InsertionPoint;
}
~MPSCLockFreeQueue()
{
// release the list
T result;
while( Consume(result) )
{
//The list should be cleaned up before the destructor is called as there is no way to know whether or not to delete the value.
//So we just do our best.
}
}
void Produce( const T& t )
{
Node * node = new Node(t);
Node * oldInsertionPoint = (Node *) InterLockedxChange((volatile void **)&InsertionPoint,node);
oldInsertionPoint->next = node;
}
bool Consume( T& result )
{
if (Head->next)
{
Node * oldHead = Head;
Head = Head->next;
delete oldHead;
result = Head->value;
return true;
}
return false; // else report empty
}
};