C++ C++;这个MPMC队列中的原子需要什么内存顺序?
这是我仅使用原子实现的无锁、无等待、多线程安全MPMC队列:C++ C++;这个MPMC队列中的原子需要什么内存顺序?,c++,C++,这是我仅使用原子实现的无锁、无等待、多线程安全MPMC队列: template <typename T, int32_t SIZE> class queue_mpmc { T ar[SIZE]; std::atomic_uint64_t start; std::atomic_uint64_t end; std::atomic_int32_t precount; std::atomic_int32_t postcount; public:
template <typename T, int32_t SIZE>
class queue_mpmc {
T ar[SIZE];
std::atomic_uint64_t start;
std::atomic_uint64_t end;
std::atomic_int32_t precount;
std::atomic_int32_t postcount;
public:
queue_mpmc() {
start = 0;
end = 0;
precount = 0;
postcount = 0;
}
bool trypush(T data) {
int32_t c = ++precount; // this must be done before the line beginning ar
if (c > SIZE) { [[unlikely]]
--precount;
return false;
}
ar[end++ % SIZE] = data;
++postcount; // this must be done after the line beginning ar
return true;
}
T trypop() {
int32_t c = --postcount; // this must be done before the line beginning ar
if (c < 0) {
++postcount;
return T{};
}
T r = ar[start++ % SIZE];
--precount; // this must be done after the line beginning ar
return r;
}
int32_t len() {
return precount;
}
};
模板
类队列{
tar[大小];
std::原子电路64 t启动;
std::原子uint64终端;
std::原子int32预计数;
标准::原子计数;
公众:
队列_mpmc(){
开始=0;
结束=0;
预计数=0;
后计数=0;
}
布尔特里普什(T数据){
int32_t c=++precount;//这必须在行开始之前完成
如果(c>大小){[[不太可能]]
--预计数;
返回false;
}
ar[end++%SIZE]=数据;
++postcount;//这必须在行开始后执行
返回true;
}
T trypop(){
int32_t c=--postcount;//这必须在行开始之前完成
if(c<0){
++后计数;
返回T{};
}
tr=ar[start++%SIZE];
--precount;//这必须在行开始后执行
返回r;
}
int32_t len(){
返回预计数;
}
};
它的大小设计为2(32、64、128、256等),因为在这些情况下,由于编译器优化,它使用模运算符在底层数组上提供了有效的环绕
我一直在读关于原子的书,但我对这是一个新手,我不知道这是否适用于我正在使用的原子?设计依赖于++/--precount
和++/--postcount
在end++
和start++
指令之前或之后(在ar
开头的行中)。尽管如此,这只需要应用于当前线程,它不需要在线程之间进行超出原子学通常预期的同步
我可以通过指定内存顺序来提高性能吗?如果其他错误已修复,则最有可能的是
开始
,结束
,以及预计数
在所有使用中都可以释放内存顺序
postcount
是表示值已写入队列的变量。ie控制值的发布和读取
因此,postcount
需要在push中释放内存顺序(“发布”),并在读取之前获取内存顺序(即在pop中)
可能。两个线程同时推送,第一个线程结束+,因此将填充ar[0]。第二个执行end++,得到ar[1]。第二个线程首先完成,增加后计数。第一个线程尚未填充ar[0]。现在出现了一条读者线索。查看postcount>0,读取ar[0],其中实际上没有t。在担心内存顺序之前,可能先解决这个问题。另外,理论上,如果一个生产者在end++之后暂停太久,以至于其他线程导致队列环绕到同一位置,那么至少两个生产者可以写入同一位置。@tony,这是一个困难的问题(第一个),有什么建议吗?有没有办法确保线程在重新调度之前完成一定量的执行?关于无锁的问题是,你不能保证/保证任何事情。