C++ 通过自动解锁从类返回锁定的资源

C++ 通过自动解锁从类返回锁定的资源,c++,c++11,thread-safety,C++,C++11,Thread Safety,我想要一个类成员函数,它返回一个指向资源的指针。资源应自动锁定和解锁。我考虑创建一个不可复制的对象来处理锁定 你认为以下是一个好的解决方案吗?它是线程安全的吗?STL中是否已经有用于此用例的工具 template<typename T, typename M> struct LockedResource { private: T* data_; std::unique_lock<std::mutex> lock_; public: LockedR

我想要一个类成员函数,它返回一个指向资源的指针。资源应自动锁定和解锁。我考虑创建一个不可复制的对象来处理锁定

你认为以下是一个好的解决方案吗?它是线程安全的吗?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_; }
};
模板
结构锁定资源
{
私人:
T*数据;
std::唯一锁;
公众:
LockedRessource(T*data,M&mtx):数据{data},锁{mtx}{}
T*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();
}
#包括
#包括
#包括
#包括
福班{
私人:
std::矢量数据;
std::互斥mtx;
公众:
LockedResource数据()
{return LockedResource{&data,mtx};}
};
富富,;
无效辅助对象(int辅助对象、int迭代、int dt){

对于(inti=0;i,提供一个句柄来封装对同步对象的访问和必要的锁定这一想法并不新鲜:请参阅

我喜欢,所以我想出了一个:

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

注意正确地同步副本/移动/交换。

考虑一个带有自定义删除器的
std::unique_ptr
实例。@Rook你能详细阐述一下这个想法吗?不过,我不确定这是个好主意。问题是,自定义删除器需要可复制,所以它不能拥有类似
std::unique_lock
。这意味着客户删除程序必须持有对拥有数据资源的
Foo
类的引用,并能够直接操作其
mtx\ucode>属性。这让我感觉不好,我担心生成的代码的线程和异常安全性。我会再考虑一下@Rook为
unique\u ptr
创建一个客户删除器不需要可复制,可移动性就足够了。@Casey是这样想的,但我似乎无法创建一个包含
unique\u lock
的删除器,而不会因为删除的函数而出错。也许我应该就这件事问一个单独的问题。我应该更彻底一些我的想法有时是值得的;)
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);
}