RAII 等效于 FIFO 发布订单

RAII equivalent for FIFO release order

本文关键字:布订单 FIFO RAII      更新时间:2023-10-16

RAII 非常舒适,我很难为必须以与获取顺序相同的顺序 (FIFO( 而不是以相反的顺序(堆栈(从 RAII 中自然产生的资源提供等效的设计。

在我的具体情况下,我有一个stream类如下:

template<typename T>
class stream {
...
public:
// Producer API
T& write_acquire(); // This acquires a storage element and will "block"
// until a slot is available in the underlying resource
void write_release(&T); // This releases the storage element, transferring the data to a consumer
// Consumer API
T& read_acquire(); // This acquires a storage element and will "block"
// until a slot has been write_release
void read_release(&T); // This releases the storage element making it available
// for a potential future write_acquire
};

我正在考虑提供一个 RAII 风格的助手:

template<typename T>
class stream_wslot {
stream<T> &s;
T &slot;
public:
stream_wslot(stream<T> &s) : s{s}, slot{s.write_acquire()} {}
~stream_wslot() { s.write_release(slot); }
operator T&() { return slot; }
T& operator=(T &val) { return slot = val; }
};

但问题是以下用法将无法正确运行:

void test(stream<float> &fifo) {
stream_wslot even(fifo);
stream_wslot odd(fifo);
... first ...
... second ...
// releases odd !!!
// releases even
}

也就是说,我们将在释放even槽之前释放odd槽。虽然我可以在stream中添加一个"重新排序"队列,但我想知道是否有一种"干净"的方法可以将 RAII 推广到这些情况。

使用std::optional,代价非常适中,开销最小,提供了对构造的更多控制,以及定义明确的销毁顺序。这正是您正在寻找的,在这里。

例如:

std::optional<stream_wslot> o_even;
std::optional<stream_wslot> o_odd;
o_odd.emplace(fifo);
o_even.emplace(fifo);
auto &even=*o_even;
auto &odd=*o_odd;

从这一点开始,使用oddeven的现有代码将很难区分。总计:odd首先建造,even其次建造。odd首先被摧毁,当离开这个范围时,even被摧毁。同样有效的建造和破坏命令。

这是一个使用std::queue并在 C++11 及更高版本中工作的替代方案:

#include <queue>
void test(stream<float> &fifo) {
std::queue<stream_wslot> wslots;
#if __cplusplus >= 201703L                             // C++17 and later:
auto& even = wslots.emplace(fifo);
auto& odd = wslots.emplace(fifo);
#else                                                  // C++11 and C++14:
auto& even = (wslots.emplace(fifo), wslots.front());    
auto& odd = (wslots.emplace(fifo), wslots.front());
#endif
// work with even and odd ...
}

首先创建even引用的元素,然后创建odd引用的元素。

在作用域结束时,std::queue首先释放even引用的元素,然后释放odd引用的元素。

您可以在另一个线程上运行一个队列,以异步方式实现 FIFO 逻辑。如果wslot对象是使用该队列的引用构造的,则可以告诉它在销毁时为它们执行write_release(),但对构造执行常规write_acquire(),如有必要,这将阻止。

(或者,在建设时,他们可以在某个地方通过一个std::future,并通过write_release()破坏来实现承诺。但这只是我含糊不清的挥手(。