c - 带有 Gcc 内置函数的 LockFree 队列
问题描述
我正在开发一个单独的写入/读取器 lock_free 队列,该队列放置在共享内存上,该内存将由两个不同的 Linux 进程打开。两个进程都使用 MAP_SHARED 打开 shm。
队列如下所示:
typedef stuct {
unsigned char Data[ 256 ];
} Element_Type;
typedef struct {
unsigned int Snd_Cnt;
unsigned int Rcv_Cnt;
Element_Type Elems[ 100 ];
} Queue_Type;
OBS:两个 Linux 进程打开相同的共享内存并将其视为指向 Queue_Type 的指针。假设指针是 Shm_p。
作为编写器的 Linux 进程是这样的:
Tmp_Snd_Cnt = (Shm_p->Snd_Cnt + 1u) % 100;
__atomic_load(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
memcpy(&Shm_p->Elems[ Tmp_Snd_Cnt ].Data[ 0 ], pointer_to_real_data, size_of_real_data <= 256);
__atomic_store(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_RELEASE);
}
作为读者的 Linux 进程是这样的:
Tmp_Rcv_Cnt = Shm_p->Rcv_Cnt;
__atomic_load(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
pointer_to_real_data = &Shm_p->Elems[ Tmp_Rcv_Cnt ].Data[ 0 ];
Tmp_Rcv_Cnt = (Tmp_Rcv_Cnt + 1u) % 100;
__atomic_store(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_RELEASE);
}
我的问题是,您是否看到像比赛和其他东西这样的方法有任何问题?
我所看到的是以下内容:有时读者会获取 pointer_to_real_data 以指向未更新的数据。这表明 GCC 已将 memcpy 放在 atomic_store 之后(即编译时指令重新排序)。为了缓解这种情况,我asm volatile("" : : : "memory")
在编写器代码中的原子存储之前放置了。读者得到了正确的数据。
为了让事情变得更奇怪,我拿走了 asm volatile 并再次编译。代码开始工作。(我在再次测试之前已经清除了 shm)。
所以很明显,当读者获得未更新的数据时,编译器在第一种情况下做了一些奇怪的事情,而没有 asm volatile。可能是什么?
现在,为了安全起见,我在那里有 asm volatile 以指示编译器不要重新排序代码。
提前致谢。
稍后编辑(为了减轻在存储 Rcv_Cnt 后阅读器变慢的情况下覆盖指针到真实数据的问题):
typedef stuct {
unsigned char Data[ 256u ];
} Base_Element_Type;
typedef stuct {
unsigned int Idx;
Base_Element_Type Base_Elems[ 2u ];
} Element_Type;
typedef struct {
unsigned int Snd_Cnt;
unsigned int Rcv_Cnt;
Element_Type Elems[ 100u ];
} Queue_Type;
编写器代码:
Tmp_Snd_Cnt = (Shm_p->Snd_Cnt + 1u) % 100u;
__atomic_load(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
Elem_p = &Shm_p->Elems[ Tmp_Snd_Cnt ];
memcpy(&Elem_p->Base_Elems[ Elem_p->Idx ].Data[ 0u ], pointer_to_real_data, size_of_real_data <= 256u);
__atomic_store(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_RELEASE);
}
阅读器代码:
Tmp_Rcv_Cnt = Shm_p->Rcv_Cnt;
__atomic_load(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
Elem_p = &Shm_p->Elems[ Tmp_Rcv_Cnt ];
pointer_to_real_data = &Elem_p->Base_Elems[ Elem_p->Idx ].Data[ 0u ];
Elem_p->Idx = (Elem_p->Idx + 1u) % 2u;
Tmp_Rcv_Cnt = (Tmp_Rcv_Cnt + 1u) % 100u;
__atomic_store(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_RELEASE);
}
这应该工作......对吧?!
解决方案
推荐阅读
- mysql - 从 SQL 数据库中提取一个整数值用于计算
- python-3.x - 使用 Anaconda 的 Sublime 3 中的 Numpy 错误
- c# - 点网核心中的存储过程
- javascript - three.js:使用 CircleGeometry 在 LineLoop 中的间隙
- google-cloud-platform - 写入 Spanner 的数据流作业中的 EOFException
- python - 是否可以从 Python 中的 CSV 行创建 GUID?
- ruby - 为什么在 Ruby 中使用 gsub 函数时,ruby 的行为就像通过引用传递?
- ios - 图表框架未加载(swift4)
- objective-c - Xcode 地图显示位置区域,但不显示位置点
- typescript - 解决 Protractor 中 browser.findElements 上的承诺的问题