在线程池 C++ 中调试 std::string 的使用

Debugging the use of std::string in a thread pool C++

我正在尝试弄清楚多线程 - 我对它还很陌生。我正在使用我发现 here 的 thread_pool 类型。对于足够大的 N,以下代码会出现段错误。你们能帮我理解为什么以及如何解决吗?


#include "thread_pool.hpp"
#include <thread>
#include <iostream>

static std::mutex mtx;
void printString(const std::string &s) {
    std::lock_guard lock(mtx);
    std::hash<std::thread::id> tid{};
    auto id = tid(std::this_thread::get_id()) % 16;
    std::cout << "thread: " << id << " " << s << std::endl;
}

TEST(test, t) {
    thread_pool pool(16);
    int N = 1000000;
    std::vector<std::string> v(N);
    for (int i = 0; i < N; i++) {
        v[i] = std::to_string(i);
    }
    for (auto &s: v) {
        pool.push_task([&s]() {
            printString(s);
        });
    }

}

这是线程清理程序的输出(注意 ===> 注释,我将您引导到适当的行”):

SEGV on unknown address 0x000117fbdee8 (pc 0x000102fa35b6 bp 0x7e8000186b50 sp 0x7e8000186b30 T257195)
0x102fa35b6 std::basic_string::__get_short_size const string:1514 
0x102fa3321 std::basic_string::size const string:970 
0x102f939e6 std::operator<<<…> ostream:1056 
0x102f9380b printString RoadRunnerMapTests.cpp:37   // ==> this line: void printString(const std::string &s) {
0x102fabbd5 $_0::operator() const RoadRunnerMapTests.cpp:49     // ===> this line: v[i] = std::to_string(i);
0x102fabb3d (test_cxx_api_RoadRunnerMapTests:x86_64+0x10001eb3d) type_traits:3694 
0x102fabaad std::__invoke_void_return_wrapper::__call<…> __functional_base:348 
0x102faba5d std::__function::__alloc_func::operator() functional:1558 
0x102fa9669 std::__function::__func::operator() functional:1732 
0x102f9d383 std::__function::__value_func::operator() const functional:1885 
0x102f9c055 std::function::operator() const functional:2560 
0x102f9bc29 thread_pool::worker thread_pool.hpp:389  // ==> [this](https://github.com/bshoshany/thread-pool/blob/master/thread_pool.hpp#L389) line
0x102fa00bc (test_cxx_api_RoadRunnerMapTests:x86_64+0x1000130bc) type_traits:3635 
0x102f9ff1e std::__thread_execute<…> thread:286 
0x102f9f005 std::__thread_proxy<…> thread:297 
0x1033e9a2c __tsan_thread_start_func 
0x7fff204828fb _pthread_start 
0x7fff2047e442 thread_start 

析构函数的调用顺序与变量声明顺序相反。即 v 将比 pool 更早被销毁,因此当池中的某些线程调用 printString() 时,参数字符串将不是有效对象,因为 v 及其内容已被销毁。要解决此问题,我建议在 pool.

之前声明 v

传递给线程池的任务包含对向量 v 内容的引用,但是该向量在池离开具有悬空引用的任务之前超出范围。为了解决这个问题,您需要重新排序变量的范围:

int N = 1000000;
std::vector<std::string> v(N);
thread_pool pool(16);