我在代码中大多数时候都会看到线程安全getter方法的这种实现的一些变体:
class A
{
public:
inline Resource getResource() const
{
Lock lock(m_mutex);
return m_resource;
}
private:
Resource m_resource;
Mutex m_mutex;
};
假设无法复制类资源,或者复制操作的计算成本太高,C ++中是否有办法避免返回副本但仍使用 RAII 风格锁定机制?
答案 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
)