简体   繁体   中英

segfault when assigning an std::thread to a vector<std::thread>

This segfaults:

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

...

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

This does not:

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

...

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

The difference in the use of push_back to the vector instead of using assignment to a specific entry in the vector.

_thFunction is an std::function.

When I apply a pool.reserve(10) to the first block of code, I still get a segfault on the first assignment.

I suspect that this is related to move semantics, but I'm not sure. What's going on here?

A stacktrace from gdb seems to indicate a null this pointer:

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

Read reference documentation of the operator[] that you're using.

Returns a reference to the element at specified location pos. No bounds checking is performed.

So, when you call _pool[n] on an empty vector, you get a reference to a non-existing object and undefined behaviour ensues. Assigning a value to this reference does not grow the vector.

reserve does not add any elements to the vector either. It simply increases the capacity of the vector which means that inserting elements to the end won't invalidate iterators / references / pointers to elements until that capacity is reached.

resize(n) would create n objects in the vector which I suspect you are expecting reserve to do and it's excellent way to initialize the vector with identical objects when you don't know the count and the value at the time the vector is constructed. But if you were to overwrite the objects anyway, push_back with reserve would waste less resources. Or, if you know the values that you want in the vector, you could simply fill the vector using the constructor.

It has nothing to do with move semantics, you're simply trying to assign to elements of a vector that don't exist.

_pool.push_back() creates a new element, but _pool[n] does not.

reserve() does not create elements either, it just allocates memory for them (but does not construct them)

Either resize the vector so the elements exist before you assign to them, or use push_back

std::vector::reserve will only "prepare" the vector to grow, but the logical size of the vector is still 0. So _pool[n] = ... is still invalid. You should use std::vector:resize instead.

Using reserve() does not create any elements - it only makes sure that when you do create them, no allocation will be performed. Create the vector with 10 empty elements like that:

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

and your first example will work

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM