简体   繁体   中英

Thread safety of multiple-reader/single-writer class

I am working on a set that is frequently read but rarely written.

class A {
  boost::shared_ptr<std::set<int> > _mySet;
public:
  void add(int v) {
    boost::shared_ptr<std::set<int> > tmpSet(new std::set<int>(*_mySet));
    tmpSet->insert(v);  // insert to tmpSet
    _mySet = tmpSet;    // swap _mySet
  }
  void check(int v) {
    boost::shared_ptr<std::set<int> > theSet = _mySet;
    if (theSet->find(v) != theSet->end()) {
      // do something irrelevant
    }
  }
};

In the class, add() is only called by one thread and check() is called by many threads. check() does not care whether _mySet is the latest or not. Is the class thread-safe? Is it possible that the thread executing check() would observe swap _mySet happening before insert to tmpSet ?

You do need synchronization, it is not thread safe. Generally it doesn't matter, even something as simple as shared += value; is not thread safe.

look here for example with regards to thread safety of shared_ptr: Is boost shared_ptr <XXX> thread safe?

I would also question your allocation/swapping in add() and use of shared_ptr in check()

update:

I went back and re-rad dox for shared_ptr ... It is most likely thread-safe in your particular since the reference counting for shared_ptr is thread-safe. However you are doing (IMHO) unnecessary complexity by not using read/write lock.

This is an interesting use of shared_ptr to implement thread safety. Whether it is OK depends on the thread-safety guarantees of boost::shared_ptr . In particular, does it establish some sort of fence or membar, so that you are guaranteed that all of the writes in the constructor and insert functions of set occur before any modification of the pointer value becomes visible.

I can find no thread safety guarantees whatsoever in the Boost documentation of smart pointers. This surprizes me, as I was sure that there was some. But a quick look at the sources for 1.47.0 show none, and that any use of boost::shared_ptr in a threaded environment will fail. (Could someone please point me to what I'm missing. I can't believe that boost::shared_ptr has ignored threading.)

Anyway, there are three possibilities: you can't use the shared pointer in a threaded environment (which seems to be the case), the shared pointer ensures its own internal consistency in a threaded environment, but doesn't establish ordering with regards to other objects, or the shared pointer establishes full ordering. Only in the last case will your code be safe as is. In the first case, you'll need some form of lock around everything, and in the second, you'll need some sort of fences or membar to ensure that the necessary writes are actually done before publishing the new version, and that they will be seen before trying to read it.

Eventually this code should be thread safe:

atomic_store(&_my_set,tmpSet);

and

theSet = atomic_load(&_mySet);

(instead of simple assignments)

But I don't know the current status of atomicity support for shared_ptr.

Note, that adding atomicity to shared_ptr in lock-free manner is really dificult thing; so even atomicity is implemented it may relay on mutexes or usermode spinlocks and, therefore, may sometimes suffer from performance issues

Perhaps, volatile qualifier for _my_set member variable should also be added.. but I'm not sure that it is strictly required by semantics of atomic operations 也许,也应该添加_my_set成员变量的volatile限定符..但是我不确定它是否是原子操作语义的严格要求

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM