首页 > 解决方案 > 从队列中返回对象的最快方法,但前提是队列有它

问题描述

假设我正在编写一个简单的线程安全队列

template <class T>
class ThreadSafeQueue
{
T pop()
    {
        std::unique_lock<std::mutex>{mutex};
        if (q.empty())
            //throw
        auto t = q.front();
        q.pop();
        return t;
    }
//...

我想pop从队列中返回一个元素,但前提是它有一个元素。目前在 m 实现中,它会引发异常。但是抛出异常是否代价高昂?我应该这样做吗?

我也可以像这样定义一个简单的类:

PossibleElement{
 T t;
 bool has = false;
}

并返回这个元素,但是当我可以访问它时,它会涉及到至少一次不必要的pop复制。T此外,当它没有返回任何元素时,它仍然会创建它。

在 ThreadSafeQueue 之外检查是否queue为空,只有如果是,调用pop也将不起作用,因为在这两个安全操作的中间它可能会丢失一个对象

标签: c++

解决方案


正如@SamVarshavchik 在他的评论中指出的那样,返回 astd::optional是处理调用时堆栈为空的意外情况的一种好方法pop()

std::optional<T> pop()
{
    std::unique_lock<std::mutex>{mutex};
    if (q.empty()) return std::nullopt;
    auto t = q.front();
    q.pop();
    return t;
}

// Calling code
std::optional<string> myVal = myQueue.pop();
if (myVal) std::cout << "Popped: " << *myVal << std::endl;

另一种可能的处理方法是返回成功/失败代码,并将弹出的对象复制到引用参数中:

bool pop(T & retVal)
{
    std::unique_lock<std::mutex>{mutex};
    if (q.empty()) return false;
    retVal = q.front();
    q.pop();
    return true;
}

// calling code
std::string myVal;
if (myQueue.pop(myVal)) std::cout << "Popped: " << myVal << std::endl;

最后,在某些用例中(例如,您不需要在队列中存储任何默认构造的对象,或者调用者不需要区分空队列和具有默认构造的队列值)你可以返回一个默认构造的对象作为默认/保护值:

T pop()
{
    std::unique_lock<std::mutex>{mutex};
    if (q.empty()) return T();
    auto t = q.front();
    q.pop();
    return t;
}

// calling code
std::string myVal = myQueue.pop();
if (myVal.size() > 0) std::cout << "Popped: " << myVal << std::endl;

推荐阅读