【问题标题】:std::shared_ptr crashing when used in threadsstd::shared_ptr 在线程中使用时崩溃
【发布时间】:2013-11-16 20:06:09
【问题描述】:

在线程 1(转述代码)中:

std::vector<std::shared_ptr<Object>> list;

// Initialization
list.reserve(prop_count);

for (size_t i = 0; i < count; ++i)
{
    list.push_back(std::shared_ptr<Object>());
}

// Looped code
for (auto iter = indexes.begin(); iter != indexes.end(); ++iter)
{
    uint32_t i = *iter;

    std::shared_ptr<Object> item = make_object(table->data[i]);  // returns a shared_ptr of Object
    list[i].swap(item);
}

在线程 2 中(转述代码):

for(auto iter = list.begin(); iter != list.end(); ++iter)
{
    shared_ptr<Property> o(*iter);

    if(o)
    {
         // some work with casting it
         // dynamic_pointer_cast
    }
}  // <--- crashes here (after o is out of scope)

这里是调用栈:

0x006ea218  C/C++
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)1>::_M_release(this = 0x505240)  C/C++
std::__shared_count<(__gnu_cxx::_Lock_policy)1>::~__shared_count(this = 0xb637dc94) C/C++
std::__shared_ptr<Property, (__gnu_cxx::_Lock_policy)1>::~__shared_ptr(this = 0xb637dc90)   C/C++
std::shared_ptr<Property>::~shared_ptr(this = 0xb637dc90)   C/C++
startSending()  C/C++
libpthread.so.0!start_thread()  C/C++
libc.so.6 + 0xb52b8 C/C++

查看shared_ptr_base.h,这里好像崩溃了:

if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
  {
        _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
    _M_dispose();  // <--- HERE

我不确定如何解决这个问题。任何帮助表示赞赏。谢谢!

【问题讨论】:

  • 一个线程修改列表,而一个线程在没有任何互斥锁的情况下读取它。为什么你认为它应该有效?
  • 它似乎并没有一直崩溃。有时它不会崩溃很长时间。其他时候它会崩溃。似乎完全是随机的。但是两个线程都在不断地访问列表。
  • 欢迎来到线程的乐趣。我建议你查一下std::mutex

标签: c++ multithreading thread-safety shared-ptr


【解决方案1】:

来自http://en.cppreference.com/w/cpp/memory/shared_ptr,我强调:

如果多个执行线程访问同一个 shared_ptr 没有同步,并且任何这些访问都使用非常量 shared_ptr 的成员函数,则将发生数据竞争;这 原子函数的 shared_ptr 重载可用于防止 数据竞赛。

在这种情况下,list[i]*iter 是相同的实例。

对于线程1,推荐std::atomic_store(&amp;list[i], item)而不是list[i].swap(item)

对于线程 2,推荐std::shared_ptr&lt;Property&gt; o(std::atomic_load(&amp;*iter)) 而不是std::shared_ptr&lt;Property&gt; o(*iter);

这一切都假设向量的大小不会改变,并引入容器线程安全、迭代器无效等问题。不过,这超出了本问题的范围,并在其他地方进行了讨论。

【讨论】:

    【解决方案2】:

    1) 将数据放入容器:使用队列,而不是向量。不要保留和交换,只需将它们 push() 到队列中。 2) 每次推送都需要一个互斥体(类成员)保护。

    ====== 第二个线程 =======

    3) 队列的pop值,每个pop都需要和上面一样的mutex保护。

    见:Using condition variable in a producer-consumer situation

    【讨论】:

      猜你喜欢
      • 2014-09-08
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 2013-01-07
      • 1970-01-01
      相关资源
      最近更新 更多