在多线程世界中使用std :: sort与shared_ptr向量的危险

时间:2015-12-15 12:42:45

标签: multithreading sorting c++11 boost vector

这是我将应用程序升级到多线程世界时遇到的问题的简化版本(C ++ 11)。基本上我有shared_ptr的向量,我正在做std :: sort。当多个线程尝试对它进行排序时,我可以理解,它在排序时很危险,第一次,迭代器可能需要四处移动。但是,在这里,我已经有一个排序的矢量。现在打电话,std :: sort就不应该给它带来任何麻烦(这就是我认为没什么需要移动的)但它随机地崩溃了(现在为什么我称之为std ::对已排序的容器进行排序,实际上,在原始代码中,数据是未排序的,但这对最终结果似乎并不重要)。这是示例代码

#include <iostream>
#include <thread>
#include <vector>
#include <boost/shared_ptr.hpp>  
const int MAX = 4;
#define LOOP_COUNT 200

struct Container {
    int priority;
    Container(int priority_)
        : priority( priority_)
    {} 
};

struct StrategySorter {
    int operator()( const boost::shared_ptr<Container>& v1_,
        const boost::shared_ptr<Container>& v2_ )
    {
      return v1_->priority > v2_->priority;    
    } 
 };

std::vector<boost::shared_ptr<Container>> _creators;

void func() {    
    for(int i=0; i < LOOP_COUNT; ++i)    {
      std::sort( _creators.begin(), _creators.end(),  StrategySorter() );
    } 
}
int main()
{

   int priority[] = {100, 245, 312, 423, 597, 656, 732 };
   size_t size = sizeof(priority)/sizeof(int);

   for(int i=0; i < size; ++i)
   {
      _creators.push_back(boost::shared_ptr<Container>(new Container(priority[i])));
   }

   std::thread t[MAX];
   for(int i=0;i < MAX; i++)
   {
      t[i] = std::thread(func);
   }

   for(int i=0;i < MAX; i++)
   {
      t[i].join();
   }
}

错误: ../boost_1_56_0/include/boost/smart_ptr/shared_ptr.hpp:648:typename boost :: detail :: sp_member_access :: type boost :: shared_ptr :: operator-&gt;()const [with T = Container; typename boost :: detail :: sp_member_access :: type = Container *]:断言`px!= 0&#39;失败。

拥有原始指针并不会使其崩溃,因此它特定于shared_ptr。 使用互斥锁保护std :: sort可防止崩溃。 我无法理解为什么这种情况会导致不一致的行为。

2 个答案:

答案 0 :(得分:2)

当多个线程在没有同步的情况下访问相同的数据并且其中至少有一个正在进行修改操作时,它就是竞争条件,因此是未定义的行为。任何事情都可能发生。

std::sort需要可变迭代器来操作,因此根据定义它是一个修改操作,因此在同步时将它同时应用于重叠范围是竞争条件(因而是UB)。

答案 1 :(得分:1)

无法保证最终不会移动元素的排序不会写入。

它可能希望移动枢轴,在中间阶段向后排序某些东西,甚至在没有自检优化的情况下调用swap(a,a)(因为检查可能比交换更昂贵)。

在任何情况下,操作如果它什么都不做就是UB是一个可怕的操作来调用。

如果不采取任何措施,可以保证什么都不做:

template<class C, class Cmp>
void my_sort( C& c, Cmp cmp ) {
  using std::begin; using std::end;
  if (std::is_sorted( begin(c), end(c), cmp ))
    return;
  std::sort( begin(c), end(c), cmp );
}

但我不会用它。