C++线程安全访问器和赋值器

bxfogqkk  于 2023-03-20  发布在  其他
关注(0)|答案(1)|浏览(146)

我有一个建筑师类似下面的代码.

struct Object {
  Object(int& n) : value{n} {}
  int& value;
}

struct Accessor {
  Accessor(const int& o) : o_(o) {}

  const int& getValue() {
   return o_;
  }

  private:
    const int& o_;
}

struct Mutator {
  Mutator(int& o) : o_(o) {}

  int& getValue() {
   return o_;
  }
  private:
    int& o_;
}

struct ObjectA : Object {
  shared_ptr<const Accessor> getAcc() {
    return make_shared<Accessor>(Accessor{value});

  shared_ptr<Mutator> getMut() {
    return make_shared<Mutator>(Mutator{value});
}

目标是使用mutator写入对象值,使用accessor读取对象值。
我已经编写了单元测试,所有测试都运行良好。
我觉得它在多线程下运行时可能会有问题。我想知道mutator和accessor是否是线程安全的?如果不是,我如何改进它们使之成为线程安全的?

mf98qq94

mf98qq941#

不,您所显示的代码不是线程安全的。
按照您编写代码的方式,* 任何 * 线程都可以在 * 任何 * 时间读/写引用的int变量,而无需 * 任何 * 线程之间的同步。如果您允许一个线程在其他线程从int阅读的同时向int进行 * 写入 *,则可能发生不好的事情,因此,您必须 serialize 访问,以便写入线程获得对int的独占访问。请查看使用std::mutexstd::atomic或等效方法进行同步。
另外,Object中的int&引用是公共可访问的,而ObjectA是从Object公共继承的,所以引用的int对所有外部代码都是公共可访问的,因此 * 任何 * 线程都可以完全绕过访问器/赋值器。使得X1 M13 N1 X是私有的而不是公共的,并且 * 不 * 让accessor/mutator返回对int的引用。这只允许调用方获取引用,然后在需要时直接访问int。在accessor/mutator类来强制对int的所有访问都只通过它们。
试试这样的方法:

#include <mutex>

struct Object {
  Object(int& n) : value{n} {}
  int& value;
};

struct Accessor {
  Accessor(const int& o, std::mutex &m) : o_(o), m_(m) {}

  int getValue() {
    std::lock_guard<std::mutex> lock(m);
    return o_;
  }

  /*
  operator int() {
    std::lock_guard<std::mutex> lock(m);
    return o_;
  }
  */

  private:
    const int& o_;
    std::mutex& m_;
}

struct Mutator {
  Mutator(int& o, std::mutex &m) : o_(o), m_(m) {}

  void setValue(int value) {
    std::lock_guard<std::mutex> lock(m);
    o_ = value;
  }

  /*
  Mutator& operator=(int value) {
    std::lock_guard<std::mutex> lock(m);
    o_ = value;
    return *this;
  }
  */

  private:
    int& o_;
    std::mutex& m_;
};

struct ObjectA : private Object {

  ObjectA() : Object(value_) {}

  shared_ptr<Accessor> getAcc() const {
    return make_shared<Accessor>(value_, lock_);
  }

  shared_ptr<Mutator> getMut() {
    return make_shared<Mutator>(value_, lock_);
  }

  private:
    int value_ = 0;
    std::mutex lock_;
};

...

ObjectA o;
int val = o.getAcc()->getValue();
o.getMut()->setValue(val + 5);
/*
int val = *(o.getAcc());
*(o.getMut()) = val + 5;
*/

话虽如此,你为什么要使用std::shared_ptr呢?在这类代码中没有必要使用std::shared_ptr。你的getter/mutator可以直接通过值而不是动态指针返回类对象,例如:

#include <mutex>

struct Object {
  Object(int& n) : value{n} {}
  int& value;
};

struct Accessor {
  Accessor(const int& o, std::mutex &m) : o_(o), m_(m) {}

  int getValue() {
    std::lock_guard<std::mutex> lock(m);
    return o_;
  }

  /*
  operator int() {
    std::lock_guard<std::mutex> lock(m);
    return o_;
  }
  */

  private:
    const int& o_;
    std::mutex& m_;
}

struct Mutator {
  Mutator(int& o, std::mutex &m) : o_(o), m_(m) {}

  void setValue(int value) {
    std::lock_guard<std::mutex> lock(m);
    o_ = value;
  }

  /*
  Mutator& operator=(int value) {
    std::lock_guard<std::mutex> lock(m);
    o_ = value;
    return *this;
  }
  */

  private:
    int& o_;
    std::mutex& m_;
};

struct ObjectA : private Object {

  ObjectA() : Object(value_) {}

  Accessor getAcc() {
    return Accessor{value_, lock_};
  }

  Mutator getMut() {
    return Mutator{value_, lock_};
  }

  private:
    int value_ = 0;
    std::mutex lock_;
};

...

ObjectA o;
int val = o.getAcc().getValue();
o.getMut().setValue(val + 5);
/*
int val = o.getAcc();
o.getMut() = val + 5;
*/

尽管如此,在这种情况下,我并不建议使用单独的access/mutator类,因为它们有点多余,例如:

#include <mutex>

struct Object {
  Object(int& n) : value{n} {}
  int& value;
};

struct ObjectA : private Object {

  ObjectA() : Object(value_) {}

  int getValue() const {
    std::lock_guard<std::mutex> lock(lock_);
    return value_;
  }

  void setValue(int value) {
    std::lock_guard<std::mutex> lock(lock_);
    value_ = value;
  }

  private:
    int value_ = 0;
    mutable std::mutex lock_;
};

...

ObjectA o;
int val = o.getValue();
o.setValue(val + 5);

或者:

#include <atomic>

struct Object {

  int getValue() const {
    return value_;
  }

  void setValue(int value) {
    value_ = value;
  }

  private:
    std::atomic<int> value_{0};
};

...

Object o;
int val = o.getValue();
o.setValue(val + 5);

相关问题