c++ - 在 C++17 中,是否未定义使用无锁原子保护从信号处理程序传递的数据?
问题描述
根据我对过去问题的阅读,该标准允许在信号处理程序中访问无锁原子,并且对“普通”对象的任何修改都变得未定义。如果是这样,下面的模式是不是原子变量是非原子变量的内存屏障,也是未定义的?
下面的模式是单生产者-信号-消费者环形缓冲区的模式。信号是生产者,线程是消费者。请忽略正在旋转的收集线程的低效率 - 不想使已经复杂的示例复杂化。
AFAICT,这种模式在信号处理程序中使用是安全的,因为原子变量可以防止任何竞争,并且我避免了信号处理程序内部的内存分配。但是,它确实修改了信号处理程序中的非原子对象。
vector<vector<int>> ring;
atomic<int> push_idx;
atomic<int> pop_idx;
void init()
{
ring.resize(8);
for (auto &v : ring)
{
v.resize(10);
}
}
int advance_idx(int i)
{
return (i + 1) % 8;
}
int buf[5];
void sig_handler()
{
int a = push_idx.load(memory_order_relaxed);
int p = pop_idx.load(memory_order_acquire);
if (advance_idx(a) == p)
{
return; // ring overflow
}
// fill up static-linkage data
for (int i = 0; i < 5; i++)
{
buf[i] = i;
}
// copy some static-linkage data into buffer
for (int i = 0; i < 5; i++)
{
ring[a][i] = buf[i];
}
ring[a].resize(5);
push_idx.store(advance_idx(a), memory_order_release)
}
void collecting_thread()
{
for (;;)
{
int p = pop_idx.load(memory_order_relaxed);
int a = push_idx.load(memory_order_acquire);
if (p == a)
{
continue; // nothing to do
}
auto &vec_to_process = ring[p];
process_vec(std::move(vec_to_process); //may move-from
vec_to_process.clear();
vec_to_process.resize(10);
pop_idx.store(advance_idx(p), memory_order_release);
}
}
解决方案
根据评论回答我自己的问题 - 它似乎是未定义的行为,因为它一直都是。
正如对sem_post、信号处理程序和未定义行为(关于 POSIX)的回答中所说,它未定义不是因为它与信号、线程、原子或 CPU 的工作方式相矛盾,而是因为指定正确的行为很困难,而且没有任何意义收益。
在大多数情况下,正确处理信号的方法是在信号处理程序中设置一个标志,或者“接受信号”(sigwait() 等)。在我的情况下,这不起作用,因为我需要获取堆栈跟踪(SIGPROF 处理程序)并将其传递给应用程序。
推荐阅读
- laravel - 合并两个相似的集合
- javascript - 在函数返回之前导出触发,我该如何解决这个问题?
- c# - 在 C# 中解析复杂的多行 CSV 文件
- node.js - 将回调转换为异步的节点导致暂停执行
- javascript - 我正在尝试一次将一个组件呈现给 DOM,其中包含一组响应的对象
- apache-kafka - 给定一个偏移列表,从特定 Kafka 分区读取它们的最快方法是什么?
- db2 - 无法授予 db2 数据库任何权限
- bash - Bash:将多行数据集重塑为多列数据集
- javascript - 使用多行的 ag-grid 和自动保存
- keras - 具有不可枚举的动作输出的人工智能?