【问题标题】:Using std::atomic<T*> as a fence使用 std::atomic<T*> 作为栅栏
【发布时间】:2019-05-22 22:25:47
【问题描述】:

我在下面为异步资源管理器编写了一个 Resource 类。在加载过程中,使用默认资源(例如黑色纹理)创建一个 Resource 对象,以便使用该资源的主线程不必等待,即使它是一个空白资源。加载完成后,加载器线程将调用 setResource() 并将底层资源设置为新加载的资源。但也涉及所有权。因此,当加载完成后,新分配的资源将归 Resource 类所有,以便在 Resource 被销毁或调用另一个 setResource 时释放它,因为磁盘上的文件已更改并需要重新加载。

template <typename T>
class Resource {
 protected:
  // Underlying object
  std::atomic<T*> resource;

  // Do I own resource
  bool isOwner;

  // Id of resource for faster mapping
  uint64_t id;

  // Name
  std::string name;

 public:
  Resource(T* res) : resource(res), isOwner(false), id(0), name("non") {}

  Resource(std::unique_ptr<T>&& res)
      : resource(res.release()), isOwner(true), id(0), name("non") {}

  ~Resource() {
    if (isOwner) delete resource.load(std::memory_order_acquire);
  }

  Resource(Resource&&) = default;

  Resource(const Resource&) = delete;
  Resource& operator=(const Resource&) = delete;

  T* getResource() { 
    return resource.load(std::memory_order_acquire);
  }

  void setResource(T* res, bool own = false) {
    if (isOwner) { 
      delete resource.load(std::memory_order_acquire);
    }

    isOwner = own;
    resource.store(res, std::memory_order_release);
  }

  void setResource(std::unique_ptr<T>&& res) {
    if (isOwner) { 
      delete resource.load(std::memory_order_acquire);
    }

    isOwner = true;
    resource.store(res.release(), std::memory_order_release);
  }

  void setId(uint64_t idd) { id = idd; }
};

isOwner 上是否存在数据竞争,或者 atomic.store() 在这种情况下是否充当围栏?或者我应该改变整个所有权方法,只使用我不知道是否可行的std::atomic&lt;std::shared_ptr&lt;T&gt;&gt;

【问题讨论】:

  • hm...也许在加载时考虑“空对象模式”和“交换”
  • 你不是在这里重新实现std::unique_ptr&lt;&gt;,用一个简单的swap作为你的setResource吗?

标签: c++ concurrency atomic


【解决方案1】:

当代码执行delete resource.load(std::memory_order_acquire); 时,另一个线程可能仍在使用该资源,因此这是一个竞争条件。

一个简单的解决方法是永远不要删除现有资源。您可以将该黑色纹理作为具有静态存储持续时间的全局对象,该对象在应用程序的整个执行期间处于活动状态。

另一个,是getResource 返回一个std::shared_ptr&lt;T&gt;(按值),以便旧资源在其最后一个用户处理它时自动销毁。例如:

struct NullDeleter {
    template<class T>
    void operator()(T const&) {}
};

template<typename T>
class Resource {
protected:
    std::shared_ptr<T> resource;
public:
    Resource(T& res) : resource(&res, NullDeleter{}) {}

    Resource(std::shared_ptr<T> const& res) : resource(res) {}

    std::shared_ptr<T> getResource() {
        return atomic_load(&resource);
    }

    void setResource(std::shared_ptr<T> const& res) {
        atomic_store(&resource, res);
    }
};

【讨论】:

    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    相关资源
    最近更新 更多