簡體   English   中英

RAII 線程安全吸氣劑

[英]RAII thread safe getter

大多數時候,我在代碼中看到線程安全 getter 方法的這種實現的一些變體:

class A
{
public:

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

        return m_resource;
    }

private:
    Resource m_resource;
    Mutex    m_mutex;
};

假設類Resource不能被復制,或者復制操作的計算成本太高,在C++中有沒有辦法避免返回副本但仍然使用RAII風格的鎖定機制?

返回一個為Resource類提供線程安全接口和/或保留一些鎖的訪問器對象如何?

class ResourceGuard {
private:
    Resource *resource;

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

這將以 RAII 方式清除,並在需要時釋放任何鎖定。 如果需要鎖定,則應在Resource類中完成。

當然,您必須照顧Resource的生命周期。 一個非常簡單的方法是使用std::shard_ptr 一個 weak_ptr 也可能適合。

實現相同目的的另一種方法。 這是可變版本的實現。 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;
}

我還沒有嘗試過,但這樣的事情應該可行:

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

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

struct Resource {
    void doSomething() {printf("Resource::doSomething()\n"); }
};

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();

        // The next line will block forever as the lock is still in use
        //auto dead = a.getResource();

    } // r will be destroyed here and unlock
    a.getResource()->doSomething();
    return 0;
}

但要小心,因為被訪問資源的生命周期取決於所有者( A )的生命周期


Godbolt 示例:鏈接

P1144 很好地減少了生成的程序集,以便您可以看到鎖的鎖定和解鎖位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM