使用线程对向量进行排序

Posted

技术标签:

【中文标题】使用线程对向量进行排序【英文标题】:Sorting a vector using threads 【发布时间】:2010-03-03 02:48:10 【问题描述】:

C++ STL 中定义的向量是可重入的还是线程安全的? 我可以在不使用互斥锁的情况下使用两个线程并在向量的两半上工作(在这种情况下是排序)吗? 例如:

int size = (Data_List.size())/2;

Thread A()


................ // Do we need to lock Data_list with a mutex
 sort(Data_List.begin(),Data_List.begin()+size,cmp);


Thread B()

....// Do we need to lock Data_list with a mutex
sort(Data_List.begin()+(size+1),Data_List.end(),cmp);

我的问题是我们是否需要使用互斥锁来锁定 Data_List 的访问?

注意:cmp 函数是一个常规的 int 比较函数。

【问题讨论】:

我不知道这是否真的给你买了什么,当你完成每一半的排序时,列表仍然没有排序。您仍然需要将这两部分排序/合并在一起 @John:我正在使用 Threads 实现示例排序,然后使用递归加倍将两半合并在一起排序......我想知道我是否可以只使用偏移量并处理相同的数据结构,无需创建额外副本和单独处理副本的开销...... 好的,我同意所有其他答案 - 只要您在排序期间不更改向量中的元素数量,应该没问题。 @John - 分而治之是并行化背后的原则之一。如果数据集足够大,它当然会买东西。 @Tom:如果排序使内存带宽饱和(简单的 int 比较表明会如此),那么将每个元素移动两次只会让事情变得更糟而不是更好。对于溢出内存的排序,它可以看到这将有什么帮助,但我怀疑如果整个数据集适合一个向量,它是否会有所帮助。即便如此,他似乎知道自己在做什么,所以我现在就闭嘴;) 【参考方案1】:

只要线程在不同的内存区域工作,并且您的比较函数仅适用于该内存和局部变量,您应该没问题。本质上,您通过在线程之间划分工作并仅让线程处理其一半的数据来“锁定”表的每一半。

【讨论】:

"只让线程处理它的一半数据"。除非您的架构进行 RMW 写入,并且您的向量分区跨越了单词边界。那么这两个线程正在访问同一个内存。 @Steve Jessop -- 我假设编译器会将任何用户定义的类型填充到字边界,但对于小于机器字大小的类型,你当然需要更加小心。【参考方案2】:

差不多,但不完全。这段代码通常不是线程安全的,即使假设实现对vector的线程安全做出了合理的保证。

假设Data_List::value_type 是您的架构不提供原子访问的类型。例如,在 x86 上,字节写入和对齐的字和双字写入是原子的,但短写入不是,用户定义类型的写入也不是,除非它们恰好具有良好的大小和对齐方式。如果您的 UDT 大小为 3,您可能会遇到问题。

要执行非原子短写入,实现可能会执行两个字节写入。或者它可能会加载单词,修改两个字节,然后将单词写回。在字节写入代价高昂的架构上,后者相当合理。

现在,假设您的实现是后者。进一步假设您对向量的划分恰好将单词的前半部分留在一部分中,而将单词的后半部分留在另一部分中。最后假设两个不同的线程都试图同时修改那个词。这可能会出错 - 他们可能都读取相同的值,都修改它,但随后一个回写将发生在另一个之前,因此其中一个修改将被覆盖并丢失。

默认情况下的原子字节访问不是通用的,我怀疑任何实现都默认进行原子位访问。所以即使其他向量类型是安全的,vector<bool> 也是一个可能的问题:向量的划分可能会下降到一个字节的中间。并不是说您通常会以这种方式排序 bools,但是您可能会尝试在其他操作中划分向量,或者您的代码可能是通用的。

您通常可以期望字访问是原子的(在 C 或 C++ 中,int 访问)。但是语言标准并不能保证,因此sig_atomic_t。您说您的 cmp 是一个 int 比较函数,这表明您的向量可能包含 int。但是你可以使用 int 比较函数很好地比较短裤,所以我不知道。

您实际上需要做的是非常仔细地检查您的实现/平台,并查看它对从多个线程安全访问内存的内容。对于int 的向量可能没问题。

【讨论】:

@Steve:谢谢 .. 真的很感激!!! ...我决定将每个偏移量复制到单独的存储桶中并单独排序。我有点将此问题与Strtok(需要strtok_r)进行比较。所以我想知道如果我们不推入静态变量,迭代器是否会被两个线程破坏..在实现之后,它似乎没有被破坏......所以我很好..至于排序部分......我需要测试它!【参考方案3】:

您可以将 GCC 的并行模式(如果您使用 GCC)用于各种算法的线程版本。 http://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode.html

【讨论】:

【参考方案4】:

从技术上讲,标准说这些类不是线程安全的,所以如果你从 [] 运算符之类的东西中设置数据,那么从技术上讲,这是对一个对象的多次写入。另一方面,对 c 数组的不同部分进行操作是安全的……所以这里似乎存在冲突。如果您想干净利落,请获取第一个元素的地址并将其视为 c 数组。这里的很多答案都说只要你在数组的不同部分上就可以了,尽管在许多实现中这可能是正确的

->我认为重要的是要注意实现是免费的,但并不保证安全。

【讨论】:

【参考方案5】:

如果线程在向量的不相交部分工作,您应该不会有任何问题。

【讨论】:

以上是关于使用线程对向量进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Powershell命令根据线程计数对进程进行排序

在c ++中使用线程进行Shell排序[重复]

Laravel按hasmany关系排序

MFC主线程等待另一个线程结束

推力:在线程块内排序

多线程计数排序