如何以线程安全的方式迭代容器?

Mat*_*ips 9 c++ iteration containers iterator thread-safety

我有一个容器(C++),我需要以两种方式操作,来自不同的线程:1)添加和删除元素,以及2)遍历其成员.显然,在迭代发生时删除元素=灾难.代码看起来像这样:

class A
{
public:
   ...
   void AddItem(const T& item, int index) { /*Put item into my_stuff at index*/ }
   void RemoveItem(const T& item) { /*Take item out of m_stuff*/ }
   const list<T>& MyStuff() { return my_stuff; } //*Hate* this, but see class C
private:
   Mutex mutex; //Goes in the *Item methods, but is largely worthless in MyStuff()
   list<T> my_stuff; //Just as well a vector or deque
};
extern A a; //defined in the .cpp file

class B
{
   ...
   void SomeFunction() { ... a.RemoveItem(item); }
};

class C
{
   ...
   void IterateOverStuff()
   {
      const list<T>& my_stuff(a.MyStuff());
      for (list<T>::const_iterator it=my_stuff.begin(); it!=my_stuff.end(); ++it)
      {
          ...
      }
   }
};
Run Code Online (Sandbox Code Playgroud)

再次,B::SomeFunction()并被C::IterateOverStuff()异步调用.我可以使用什么数据结构来确保在迭代期间my_stuff"保护"添加或删除操作?

Eva*_*ran 15

听起来像是一个读/写锁.基本上,这个想法是你可能有一个或多个读者一个作家.你永远不能同时拥有读写锁.

编辑:我认为适合您的设计的一个使用示例涉及做一个小的改变.将"iterate"函数添加到拥有列表的类中并使其模板化,以便您可以传递函数/函数来定义要为每个节点执行的操作.像这样的东西(快速和脏的伪代码,但你明白了......):

class A {
public:
    ...
    void AddItem(const T& item, int index) {
        rwlock.lock_write();
        // add the item
        rwlock.unlock_write();
    }

    void RemoveItem(const T& item) {
        rwlock.lock_write();
        // remove the item
        rwlock.unlock_write();
    }

    template <class P>
    void iterate_list(P pred) {
        rwlock.lock_read();
        std::for_each(my_stuff.begin(), my_stuff.end(), pred);
        rwlock.unlock_read();
    }

private:
    rwlock_t rwlock;
    list<T> my_stuff; //Just as well a vector or deque
};


extern A a; //defined in the .cpp file

class B {
    ...
    void SomeFunction() { ... a.RemoveItem(item); }
};

class C {
    ...

    void read_node(const T &element) { ... }

    void IterateOverStuff() {
        a.iterate_list(boost::bind(&C::read_node, this));
   }
};
Run Code Online (Sandbox Code Playgroud)

另一种选择是使读/写锁可公开访问,并让调用者负责正确使用锁.但这更容易出错.

  • +1.Boost有以下实现:http://www.boost.org/doc/libs/1_46_1/doc/html/thread/synchronization.html#thread.synchronization.mutex_types.shared_mutex (5认同)

rav*_*int 5

恕我直言,在数据结构类中有一个私有互斥锁然后编写类方法是错误的,这样无论调用方法的代码做什么,整个事情都是线程安全的。完全和完美地做到这一点所需的复杂性是最重要的。

更简单的方法是拥有一个公共(或全局)互斥锁,当调用代码需要访问数据时,它负责锁定。

是我关于这个主题的博客文章。