使用std :: unique_ptr/std :: shared_ptr确认线程安全性

ZXc*_*bnM 3 thread-safety shared-ptr unique-ptr c++11

我的应用程序有一个基本上是普通客户端的IRC模块.由于这是严格的线程,我冒着插件检索的风险,例如,用户昵称 - 它当时有效,但解析器触发更新,更改所述昵称.一旦另一个线程再次执行,它就会处理一个指向现在无效内存的指针,因为将返回+副本作为原子操作是不可能的.

基于以下代码,我的假设是正确的吗?因此,我想我必须使用通常的互斥锁定/解锁方法,除非有人可以确认或建议否则(我宁愿不必转换并返回shared_ptr,但我想这是一个有效的选项,它是只是我打算SWIG这个,不知道它是否不喜欢它们.

IrcUser.h

class IrcUser : public IrcSubject
{
private:
    ...
    std::shared_ptr<std::string>    _nickname;
    std::shared_ptr<std::string>    _ident;
    std::shared_ptr<std::string>    _hostmask;
public:
    ...
    const c8*
    Ident() const
    { return _ident.get()->c_str(); }

    const c8*
    Hostmask() const
    { return _hostmask.get()->c_str(); }

    const u16
    Modes() const
    { return _modes; }

    const c8*
    Nickname() const
    { return _nickname.get()->c_str(); }

    bool
    Update(
        const c8 *new_nickname,
        const c8 *new_ident,
        const c8 *new_hostmask,
        const mode_update *new_modes
    );
};
Run Code Online (Sandbox Code Playgroud)

IrcUser.cc

bool
IrcUser::Update(
    const c8 *new_nickname,
    const c8 *new_ident,
    const c8 *new_hostmask,
    const mode_update *new_modes
)
{
    if ( new_nickname != nullptr )
    {
        if ( _nickname == nullptr )
        {
            *_nickname = std::string(new_nickname);
        }
        else
        {
            _nickname.reset();
            *_nickname = std::string(new_nickname);
        }

        Notify(SN_NicknameChange, new_nickname);
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 8

我建议锁定如此细粒度的水平可能(方式)矫枉过正.

我建议对IrcUser对象本身进行原子更新,根据你的库实现和目标架构,它可以是无锁的.这是一个使用的示例

  • std::atomic_is_lock_free<std::shared_ptr>
  • std::atomic_load<std::shared_ptr>
  • std::atomic_store<std::shared_ptr>

有关文档,请参见http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic.

免责声明 我不知道有多少编译器/ C++库实现已经实现了这个C++ 11功能.

这是它的样子:

#include <atomic>
#include <memory>
#include <string>

struct IrcSubject {};
typedef char c8;
typedef uint16_t u16;
typedef u16 mode_update;

class IrcUser : public IrcSubject
{
    private:
        // ...
        std::string _nickname;
        std::string _ident;
        std::string _hostmask;
        u16         _modes;
    public:
        IrcUser(std::string nickname, std::string ident, std::string hostmask, u16 modes)
            : _nickname(nickname), _ident(ident), _hostmask(hostmask), _modes(modes) { }
        // ...
        std::string const& Ident()    const { return _ident; }
        std::string const& Hostmask() const { return _hostmask; }
        const u16          Modes()    const { return _modes; }
        std::string const& Nickname() const { return _nickname; }
};

//IrcUser.cc
bool Update(std::shared_ptr<IrcUser>& user,
    std::string new_nickname,
    std::string new_ident,
    std::string new_hostmask,
    const mode_update *new_modes
)
{
    auto new_usr = std::make_shared<IrcUser>(std::move(new_nickname), std::move(new_ident), std::move(new_hostmask), *new_modes /* ??? */);
    std::atomic_store(&user, new_usr);
    //Notify(SN_NicknameChange, new_nickname);
    return true;
}

bool Foo(IrcUser const& user)
{
    // no need for locking, user is thread safe
}

int main()
{
    auto user = std::make_shared<IrcUser>("nick", "ident", "hostmask", 0x1e);

    mode_update no_clue = 0x04;
    Update(user, "Nick", "Ident", "Hostmask", &no_clue);

    {
        auto keepref = std::atomic_load(&user);
        Foo(*keepref);
    }
}
Run Code Online (Sandbox Code Playgroud)