C++设置返回值互斥体

问题描述 投票:0回答:1

在使用 FreeRTOS 在 ESP32 上运行的应用程序中,我实现了一个负责存储各种设置的设置类。我目前担心这个类的线程安全性,特别是关于“Get”方法。

有人可以检查我的实现并确认它是否正确且线程安全吗?我怀疑“复制”是否总是真正的复制,尤其是当设置类型是字符串或对象时。

template<typename T>
class Setting : public ISetting
{
    Mutex& mutex;
    T Value;

public:
    Setting(Mutex& mutex, const T& defaultValue)
        : mutex(mutex)
        , Value(defaultValue)
    {
    }

    void Set(const T& value)
    {
        mutex.Take();
        Value = value; // Assign the new value to the setting
        mutex.Give();
    }

    T Get()
    {
        T copy;
        mutex.Take();
        copy = Value;
        mutex.Give();
        return copy;
    }

    void Accept(ISettingVisitor* handler)
    {
        handler->Visit(this);
    }
};
c++ return copy settings mutex
1个回答
0
投票

Get()
是线程安全的,因为
T
的复制赋值运算符的执行仅由一个线程同时完成。但是,总体来说并不安全。

异常安全

Take()
Give()
是非常不寻常的锁定/解锁名称;对于
std::mutex
,它们被称为
.lock()
.unlock()
。 如果像这样调用它们,您可以使用
std::scoped_lock
而不是手动锁定/解锁:

void Set(const T& value)
{
    std::scoped_lock lock{mutex}; // or std::lock_guard before C++17
    Value = value;
}

T Get()
{
    std::scoped_lock lock{mutex}; // or std::lock_guard before C++17
    return Value;
}

或者,可以使用

std::experimental::scope_exit
或其他方式来解锁析构函数中的互斥体。

无论如何,在析构函数中解锁互斥体“非常重要”,因为这使得异常安全变得容易。如果 mutex 抛出异常,则代码中的

copy = Value
永远不会解锁,并且复制赋值运算符可以合理地做到这一点。同样的问题也适用于
Set()
如果 

T

具有引用语义,则不安全
如果

T

没有值语义,那么这段代码仍然不是

真正
线程安全的。例如,如果 T = std::span,则调用者可以同时且不安全地修改跨度引用的数据。复制
std::span
就是所谓的“浅复制”;它不会复制底层数据(“深层复制”)。
但是,这不是您可以解决的问题。无法检查给定类型的语义是什么。确保整体安全使用是

Setting

用户的责任。

Setting
的职责只是线程安全地获取和设置一些值。
    

© www.soinside.com 2019 - 2024. All rights reserved.