我正在实施我自己的队列,该队列在.pop()
上阻止。此函数还接受附加参数,即超时。所以目前我有这样的代码:
template <class T>
class BlockingQueue {
private:
std::queue<T> m_queue;
std::mutex m_mutex;
std::condition_variable m_condition;
public:
T pop(uint64_t t_millis) {
std::unique_lock<std::mutex> lock(m_mutex);
auto status = m_condition.wait_for(
lock,
std::chrono::milliseconds(t_millis),
[=] {
return !m_queue.empty();
}
);
if (!status) {
throw exceptions::Timeout();
}
T next(std::move(m_queue.front()));
m_queue.pop();
return next;
};
}
其中exceptions::Timeout
是我的自定义异常。现在,从性能的角度来看,我一直在考虑这个异常。从该函数返回某种返回代码会更好吗?这对性能有何影响?
此外,由于.pop
已经返回了一些内容,您将如何实现其他返回代码?我想一些包含T
和返回代码的新结构将是必需的。复杂性的增加真的值得吗?
答案 0 :(得分:4)
在未达到预期时抛出异常,在查询状态时返回状态代码。
例如:
/// pops an object from the stack
/// @returns an object of type T
/// @pre there is an object on the stack
/// @exception std::logic_error if precondition not met
T pop();
/// queries how many objects are on the stack
/// @returns a count of objects on the stack
std::size_t object_count() const;
/// Queries the thing for the last transport error
/// @returns the most recent error or an empty error_code
std::error_code last_error() const;
然后是asio风格的反应堆路线与基于执行人的期货相结合:
/// Asynchronously wait for an event to be available on the stack.
/// The handler will be called exactly once.
/// to cancel the wait, call the cancel() method
/// @param handler is the handler to call either on error or when
/// an item is available
/// @note Handler has the call signature void(const error_code&, T)
///
template<class Handler>
auto async_pop(Handler handler);
可以像这样调用:
queue.async_pop(asio::use_future).then([](auto& f) {
try {
auto thing = f.get();
// use the thing we just popped
}
catch(const system_error& e) {
// e.code() indicates why the pop failed
}
});
答案 1 :(得分:1)
在没有抛出异常的情况下,在这种情况下发出错误信号的一种方法是使用类似Andrei Alexandrescu的expected<T>
模板。
他暂时给了nice talk一段时间。我们的想法是expected<T>
包含T
,或者它包含一个异常/错误代码对象,用于描述无法生成T
的原因。
您可以使用他的实施,或根据自己的目的轻松调整想法。例如,您可以很容易地在boost::variant<T, error_code>
之上构建这样的类。
这只是另一种错误处理方式,与C风格的整数错误代码和C ++异常不同。使用变体类型并不意味着任何额外的动态分配 - 这样的代码可以是高效的,并且不会增加太多的复杂性。
答案 2 :(得分:0)
此处不需要例外。 “超时”与从队列中获取项目的结果一样正如预期的那样。 没有超时,程序基本上等同于暂停问题。假设客户端指定他们想要无限期超时。异常会抛出吗?你会如何处理这样的例外(假设你在这个后世界末日的场景中还活着?)
相反,我发现这两种设计选择更合乎逻辑(尽管它们不是唯一的选择):
阻止直到某个项目可用。创建一个名为wait
的函数,如果超时则轮询并返回false
,或者当项目可用时返回true
。您pop()
功能的其余部分可以保持不变。
不要阻止。而是返回状态:
由于您有互斥锁,因此这些选项似乎优于非等待功能。
答案 3 :(得分:0)
此外,由于.pop已经返回了一些内容,您将如何实现其他内容 返回代码?我想一些既包含T又包含返回码的新结构 需要。
采用这种方法,会对BlockingQueue
可以使用的类型提出额外要求:它们必须是默认可构造的。如果pop()
通过std::unique_ptr
返回结果(用nullptr
表示超时),则可以避免这种情况,但这会引入明显的开销。
我认为这里没有使用例外的缺点。如果以毫秒为单位测量超时,则在超时的情况下处理异常应该可以忽略不计。