RAII螺纹安全吸气剂

时间:2015-11-12 15:21:49

标签: c++ multithreading locking getter raii

我在代码中大多数时候都会看到线程安全getter方法的这种实现的一些变体:

class A
{
public:

    inline Resource getResource() const
    {
        Lock lock(m_mutex);

        return m_resource;
    }

private:
    Resource m_resource;
    Mutex    m_mutex;
};

假设无法复制类资源,或者复制操作的计算成本太高,C ++中是否有办法避免返回副本但仍使用 RAII 风格锁定机制?

3 个答案:

答案 0 :(得分:4)

如何返回一个为Resource类提供线程安全接口的访问器对象和/或保持锁定?

class ResourceGuard {
private:
    Resource *resource;

public:
    void thread_safe_method() {
        resource->lock_and_do_stuff();
    }
}

这将以RAII方式清除,并在需要时释放任何锁定。如果您需要锁定,则应在Resource类中完成。

当然,你必须照顾Resource的生命周期。一种非常简单的方法是使用std::shard_ptr。 weak_ptr也可能适合。

答案 1 :(得分:4)

实现同样的事情的另一种方式。这是可变版本的实现。 const访问器同样重要。

#include <iostream>
#include <mutex>

struct Resource
{

};

struct locked_resource_view
{
    locked_resource_view(std::unique_lock<std::mutex> lck, Resource& r)
    : _lock(std::move(lck))
    , _resource(r)
    {}

    void unlock() {
        _lock.unlock();
    }

    Resource& get() {
        return _resource;
    }

private:
    std::unique_lock<std::mutex> _lock;
    Resource& _resource;
};

class A
{
public:

    inline locked_resource_view getResource()
    {
        return {
            std::unique_lock<std::mutex>(m_mutex),
            m_resource
        };
    }

private:
    Resource m_resource;
    mutable std::mutex    m_mutex;
};

using namespace std;

auto main() -> int
{
    A a;
    auto r = a.getResource();
    // do something with r.get()

    return 0;
}

答案 2 :(得分:2)

我还没有尝试过,但这样的事情应该有效:

#include <iostream>
#include <mutex>
using namespace std;

typedef std::mutex Mutex;
typedef std::lock_guard<Mutex> Lock;

struct Resource {
    void doSomething() { std::cout << "Resource::doSomething()" << std::endl; }
};

template<typename MutexType, typename ResourceType>
class LockedResource
{
public:
    LockedResource(MutexType& mutex, ResourceType& resource) : m_mutexLocker(mutex), m_pResource(&resource) {}
    LockedResource(MutexType& mutex, ResourceType* resource) : m_mutexLocker(mutex), m_pResource(resource) {}
    LockedResource(LockedResource&&) = default;
    LockedResource(const LockedResource&) = delete;
    LockedResource& operator=(const LockedResource&) = delete;

    ResourceType* operator->()
    {
        return m_pResource;
    }

private:
    Lock m_mutexLocker;
    ResourceType* m_pResource;
};

class A
{
public:

    inline LockedResource<Mutex, Resource>&& getResource()
    {
        return LockedResource<Mutex, Resource>(m_mutex, m_resource);
    }

private:
    Resource m_resource;
    Mutex    m_mutex;
};


int main()
{
    A a;
    { //Lock scope for multiple calls
        auto r = a.getResource();
        r->doSomething();
        r->doSomething();
    } // r will be destroyed here and unlock
    a.getResource()->doSomething();
    return 0;
}

但要小心,因为所访问资源的生命周期取决于所有者的生命周期(A