我想有一个类成员函数,它返回一个指向资源的指针。应自动锁定和解锁资源。我想创建一个处理锁定的不可复制对象。
您认为以下是一个很好的解决方案吗?它是线程安全的吗? STL中是否已有用于此用例的工具?
template<typename T, typename M>
struct LockedResource
{
private:
T* data_;
std::unique_lock<std::mutex> lock_;
public:
LockedRessource(T* data, M& mtx) : data_{data}, lock_{mtx} {}
T* data() const { return data_; }
};
用例示例:
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
class Foo {
private:
std::vector<int> data_;
std::mutex mtx_;
public:
LockedResource<std::vector<int>,std::mutex> data()
{ return LockedResource<std::vector<int>,std::mutex>{&data_, mtx_}; }
};
Foo foo;
void worker(int worker, int iterations, int dt) {
for(int i=0; i<iterations; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(dt));
auto res = foo.data();
// we now have a unique_lock until the end of the scope
std::cout << "Worker " << worker << " adding " << i << std::endl;
res.data()->push_back(i);
}
}
int main() {
std::thread t1{worker, 1, 10, 173};
std::thread t2{worker, 2, 20, 87};
t1.join();
t2.join();
}
答案 0 :(得分:4)
这个想法 - 提供一个封装同步对象访问和必要锁定的句柄 - 并不是新的:见Enforcing Correct Mutex Usage with Synchronized Values。
我喜欢Rook's idea to use a unique_ptr
with a custom deleter for the handle,所以我提出了一个:
template <typename BasicLockable>
class unlock_deleter {
std::unique_lock<BasicLockable> lock_;
public:
unlock_deleter(BasicLockable& mtx) : lock_{mtx} {}
unlock_deleter(BasicLockable& mtx, std::adopt_lock_t a) noexcept : lock_{mtx, a} {}
template <typename T>
void operator () (T*) const noexcept {
// no-op
}
};
template <typename T, typename M = std::mutex>
using locked_ptr = std::unique_ptr<T, unlock_deleter<M>>;
用于实现包装对象和互斥锁的模板类synchronized
:
template <typename T, typename M = std::mutex>
class synchronized {
T item_;
mutable M mtx_;
// Implement Copy/Move construction
template <typename Other, typename N>
synchronized(Other&& other, const std::lock_guard<N>&) :
item_{std::forward<Other>(other).item_} {}
// Implement Copy/Move assignment
template <typename Other>
void assign(Other&& other) {
std::lock(mtx_, other.mtx_);
std::lock_guard<M> _{mtx_, std::adopt_lock};
std::lock_guard<decltype(other.mtx_)> _o{other.mtx_, std::adopt_lock};
item_ = std::forward<Other>(other).item_;
}
public:
synchronized() = default;
synchronized(const synchronized& other) :
synchronized(other, std::lock_guard<M>(other.mtx_)) {}
template <typename N>
synchronized(const synchronized<T, N>& other) :
synchronized(other, std::lock_guard<N>(other.mtx_)) {}
synchronized(synchronized&& other) :
synchronized(std::move(other), std::lock_guard<M>(other.mtx_)) {}
template <typename N>
synchronized(synchronized<T, N>&& other) :
synchronized(std::move(other), std::lock_guard<N>(other.mtx_)) {}
synchronized& operator = (const synchronized& other) & {
if (&other != this) {
assign(other);
}
return *this;
}
template <typename N>
synchronized& operator = (const synchronized<T, N>& other) & {
assign(other);
return *this;
}
synchronized& operator = (synchronized&& other) & {
if (&other != this) {
assign(std::move(other));
}
return *this;
}
template <typename N>
synchronized& operator = (synchronized<T, N>&& other) & {
assign(std::move(other));
return *this;
}
template <typename N>
void swap(synchronized<T, N>& other) {
if (static_cast<void*>(&other) != static_cast<void*>(this)) {
std::lock(mtx_, other.mtx_);
std::lock_guard<M> _{mtx_, std::adopt_lock};
std::lock_guard<N> _o{other.mtx_, std::adopt_lock};
using std::swap;
swap(item_, other.item_);
}
}
locked_ptr<T, M> data() & {
return locked_ptr<T, M>{&item_, mtx_};
}
locked_ptr<const T, M> data() const& {
return locked_ptr<const T, M>{&item_, mtx_};
}
};
template <typename T, typename M, typename N>
void swap(synchronized<T, M>& a, synchronized<T, N>& b) {
a.swap(b);
}
注意同时正确同步副本/移动/交换。 Here's your sample program live at Coliru