我有一个集合(目前是std::list<MyClass*>
,但也可以是std::map<int, MyClass*>
)名为my_objects
,其中包含指向MyClass
个实例的指针。
目前,我像这样处理数组:
for(std::list<MyClass*>::iterator iterator = my_objects.begin(), end = my_objects.end(); iterator != end; ++iterator) {
(*iterator)->someFunction();
// ...
}
someFunction
可能会更改当前元素的某些属性,并可能会读取其他一些元素属性。但是没有属性被更改,并被另一个实例读取。因此无论迭代次序如何,结果都是相同的。
我想重写这个循环,使用四个std::thread
,并使用第一个线程处理第一季度的元素,使用线程处理第二个季度等...
是否可以跳转到集合内部并在那里开始迭代?这是处理这样的集合的推荐方法吗?如果没有,应该怎么做?
答案 0 :(得分:3)
首先,现代C ++代码使用范围迭代:
for (const auto &ptr:my_objects)
{
ptr->someFunction();
}
更清洁,更少打字,避免常见的迭代陷阱。
现在,就分区std::list
而言,没有std::list
方法在列表中间的某处返回初始迭代器。使用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。
这会将列表整齐地划分为四个相等的部分,将列表元素分配给四个线程中的一个进行处理。
答案 1 :(得分:1)
推荐的方法是使用原子计数器为每个要处理的线程获取唯一对象。每个线程的线程函数看起来类似于此(伪代码):
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();
}
}
答案 2 :(得分:0)
在最简单的情况下,您可以创建一个工作对象(在线程内部调用),以便您通过对容器的引用以及它应该更改的范围(元素索引或迭代器)来构造它。
如果确实没有依赖于您的工作人员更改的数据部分,那么 应该工作,即使它是一个黑客。
否则,你必须在容器周围创建一种包装器对象,它实现了一个用于读取和写入特定元素的锁定机制(我建议使用boost :: upgrade_lock)。
然后,您的线程工作者对象需要与此(共享)对象进行通信,以便对容器进行读写访问。