如何使用线程处理标准容器?

How to process std containers with threads?

我有一个名为 my_objects 的集合(目前是 std::list<MyClass*>,但也可以是 std::map<int, MyClass*>),其中包含指向 MyClass 个实例的指针。

目前,我是这样处理数组的:

for(std::list<MyClass*>::iterator iterator = my_objects.begin(), end = my_objects.end(); iterator != end; ++iterator) {

    (*iterator)->someFunction();
    // ...

}

someFunction可能会改变当前元素的一些属性,也可能会读取其他元素的一些属性。但是没有 属性 被另一个实例更改和读取。所以无论迭代顺序如何,结果都是一样的。

我想重写这个循环,使用四个 std::thread,用第一个线程处理元素的第一季度,用线程处理第二个季度,等等...

是否可以跳转到集合内部,并在那里开始迭代?像这样处理集合是推荐的方法吗?如果没有,应该怎么做?

在最简单的情况下,您可以创建一个工作对象(在线程内调用),这样您就可以使用对容器的引用以及它应该的范围(元素索引或迭代器)来构造它改变。

如果您的工作人员更改的数据部分确实没有依赖性,这 应该 工作,即使它是 hack。

否则,您将不得不在您的容器周围创建一种包装对象,该对象实现了一种用于读取和写入特定元素的锁定机制(我建议 boost::upgrade_lock)。

然后,您的线程化工作对象将需要与此(共享)对象对话,以便对您的容器进行读写访问。

对于初学者,现代 C++ 代码使用范围迭代:

for (const auto &ptr:my_objects)
{
    ptr->someFunction();
}

更简洁,更少打字,避免了常见的迭代陷阱。

现在,就 std::list 的分区而言,没有 std::list 方法 returns 在列表中间某处的初始迭代器。对于 std::vector 的随机访问迭代器,这是微不足道的,但这不是 std::list 的目的。因此,如果您想将容器更改为 std::vector,这将变得轻而易举。

但是,遍历整个列表并不难,只需处理第 n 个元素即可:

size_t p=0;

for (const auto &ptr:my_objects)
{
    if (p == 0)
        ptr->someFunction();
    p = (p+1) % 4;
}

现在,此线程会为列表中的每四个元素调用 someFunction(),并且会为列表中所有元素的四分之一调用

所以,这里只需要四个线程在这里迭代,唯一不同的是第一个线程的p初始值设置为0,如图;第二个线程的初始值p设置为1;当然还有第三个和第四个的线程初始值p设置为2和3.

这会将列表整齐地分成四个相等的部分,并将列表元素分配给四个线程之一进行处理。

推荐的方法是使用原子计数器为每个线程获取唯一的对象来处理。每个线程的线程函数看起来类似于此(伪代码):

std::atomic<int> atomic_ctr(0);

void threadfunc() {
  list_iterator iter = list.begin();
  int current_list_pos=0;
  int next_pos;
  while((next_pos = atomic_ctr++) < list.size()) {
    while (current_list_pos < next_pos) {
      ++iter;
      ++current_list_pos;
    }
    iter->func();
  }
}