【问题标题】:segfault when assigning an std::thread to a vector<std::thread>将 std::thread 分配给 vector<std::thread> 时出现段错误
【发布时间】:2014-11-25 01:42:03
【问题描述】:

此段错误:

std::vector<std::thread>    _pool;
State &                     _state;

...

for(uint32_t n = 0; n < nThreads; ++n)
    _pool[n]  = std::thread(_thFunction, std::ref(_state));

这不是:

std::vector<std::thread>    _pool;
State &                     _state;

...

for(uint32_t n = 0; n < nThreads; ++n)
    _pool.push_back( std::thread(_thFunction, std::ref(_state)) );

对向量使用 push_back 而不是对向量中的特定条目进行赋值的区别。

_thFunction 是一个 std::function。

当我将 pool.reserve(10) 应用到第一个代码块时,我仍然在第一个分配上得到一个段错误。

我怀疑这与移动语义有关,但我不确定。这是怎么回事?

来自 gdb 的堆栈跟踪似乎表明 this 指针为空:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004092e5 in std::thread::joinable (this=0x0) at /usr/include/c++/4.8.3/thread:162
162     { return !(_M_id == id()); }
(gdb) backtrace
#0  0x00000000004092e5 in std::thread::joinable (this=0x0) at /usr/include/c++/4.8.3/thread:162
#1  0x000000000040927a in std::thread::operator=(std::thread&&) (this=0x0, 
    __t=<unknown type in /home/stackuser/src/dsl/build/debug/server/dsl, CU 0x0, DIE 0x37b88>)
    at /usr/include/c++/4.8.3/thread:150
#2  0x000000000041350d in ThreadPool<std::function<void (State&)> >::ThreadPool(State&, unsigned int, std::function<void (State&)>) (this=0x69aed0, state=..., nThreads=2, thFunction=...)
    at src/server/ThreadPool.h:38
#3  0x0000000000408405 in Application::Application (this=0x6946e0) at src/server/Application.cpp:69
#4  0x0000000000455594 in Singleton<Application>::CreateInstance () at src/server/Singleton.h:12
#5  0x0000000000454997 in main (argc=1, argv=0x7fffffffdc98) at src/server/main.cpp:85

【问题讨论】:

  • 保留不会为您提供可供使用的空间!!

标签: c++ multithreading c++11 segmentation-fault


【解决方案1】:

阅读您正在使用的operator[] 的参考documentation

返回对指定位置 pos 的元素的引用。不执行边界检查。

因此,当您在空向量上调用 _pool[n] 时,您会获得对不存在对象的引用,并且会出现未定义的行为。给这个引用赋值不会增长向量。

reserve 也不向向量添加任何元素。它只是增加了向量的容量,这意味着在达到该容量之前,将元素插入到末尾不会使迭代器/引用/指向元素的指针无效。

resize(n) 将在向量中创建n 对象,我怀疑您希望reserve 这样做,并且当您不知道计数和值时,这是用相同对象初始化向量的好方法构建向量的时间。但是如果你无论如何都要覆盖对象,push_backreserve 会浪费更少的资源。或者,如果您知道向量中所需的值,您可以简单地使用构造函数填充向量。

【讨论】:

  • 我说过“当我将 pool.reserve(10) 应用到第一个代码块时,我仍然在第一个分配上得到一个段错误。”这不会有所作为吗?
  • 没有。我将在答案中添加详细信息。
【解决方案2】:

std::vector::reserve 只会“准备”要增长的向量,但向量的逻辑大小仍然为 0。So _pool[n] = ... 仍然无效。你应该改用std::vector:resize

【讨论】:

  • 已确认;这行得通。总是把保留和调整大小搞混!谢谢!
  • 这很简单:reserve 只是为以后添加的元素保留空间。 resize改变大小,即改变元素个数
  • @bofjas:这是一个幼稚的观点。当然,您可能不会从该“数组”中读取段错误,但是一旦您尝试对该数组中从未构造过的元素执行操作,那么这些操作很可能会失败。在这种情况下,std::thread(赋值运算符)的内部认为它们正在对std::thread 的实例进行操作,但该实例不存在。这从 OP 的堆栈跟踪中显而易见,其中成员函数接收到 NULL this 指针,然后尝试取消引用它!
  • @bofjas: reserve not 构造任何元素。就这么简单。如何在内部实现这一点并不重要。
  • @bofjas:vector 使用Allocator::allocate,其中Allocator 是模板参数,默认为std::allocator&lt;T&gt;,其中T 是值类型模板参数。 std::allocator&lt;T&gt; 依次使用展示位置分配:void* operator new[]( std::size_t count, void* ptr );
【解决方案3】:

它与移动语义无关,您只是试图分配给不存在的向量的元素。

_pool.push_back() 创建一个新元素,但 _pool[n] 没有。

reserve() 也不创建元素,它只是为它们分配内存(但不构造它们)

resize 向量,因此元素在分配给它们之前就存在,或者使用push_back

【讨论】:

  • 我说过“当我将 pool.reserve(10) 应用到第一个代码块时,我仍然会在第一个分配上遇到段错误。”这不会有什么不同吗?
  • reserve() 也不创建元素
  • 是的,那是我的错误。
【解决方案4】:

使用reserve() 不会创建任何元素 - 它只是确保在创建它们时不会执行任何分配。用这样的 10 个空元素创建向量:

std::vector<std::thread>    _pool(10);

你的第一个例子会起作用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2014-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    相关资源
    最近更新 更多