You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++多线程操作vector不同索引string追加字符偶现coredump问题

问题现象

多线程更新全局vector<string>中不同索引对应的独立string元素,预期无并发数据冲突,但程序运行偶现coredump。
问题复现代码:

extern vector<string> gTestVec;
#define NUM 10

void * worker(void * args) {
  thread_data * p = (thread_data *)args;
  int i = p->thread_id;

  for (int j=0; j<100; j++) {
    gTestVec[i] += "a";
  }

  return NULL;
}


void do_complete_stage_test::excute() {
  int i = 0;
  pthread_t thd[NUM];
  thread_data data[NUM];

  for (i=0; i<NUM; i++) {
    gTestVec.push_back(format("%d", i));
    data[i].thread_id = i;
    
    if (0 != pthread_create(&(thd[i]), NULL, &worker, (void *)&data[i])) {
      printf("pthread_create failed");
    }
  }

  for (int i=0; i<NUM; i++) {
    if (0 != pthread_join(thd[i], NULL)) {
      printf("pthread_join failed");
    }
  }
}

崩溃时捕获的gdb栈信息:

Starting program: /data/settle_script/isp_tran_collect/bin/isp_tran_collect -p 2134234
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff2623700 (LWP 6316)]
[New Thread 0x7fffefb0e700 (LWP 6317)]
[New Thread 0x7fffef30d700 (LWP 6318)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffef30d700 (LWP 6318)]
0x00007ffff6787d67 in ?? () from /lib64/libstdc++.so.6
(gdb) bt
#0  0x00007ffff6787d67 in ?? () from /lib64/libstdc++.so.6
#1  0x00007ffff678899b in std::string::reserve(unsigned long) () from /lib64/libstdc++.so.6
#2  0x00007ffff6788bbf in std::string::append(char const*, unsigned long) () from /lib64/libstdc++.so.6
#3  0x000000000044babe in append (__s=0x880492 "a", this=<optimized out>) at /usr/include/c++/4.8.2/bits/basic_string.h:1009
#4  operator+= (__s=0x880492 "a", this=<optimized out>) at /usr/include/c++/4.8.2/bits/basic_string.h:942
#5  worker (args=<optimized out>) at ../src/do_complete_stage_test.cpp:21
#6  0x00007ffff7bc6e25 in start_thread () from /lib64/libpthread.so.0
#7  0x00007ffff5ee635d in clone () from /lib64/libc.so.6
根因分析

这是典型的std::vector并发修改导致的未定义行为,核心问题是线程创建和vector元素插入在同一个循环中执行,但线程启动后会立刻并发访问vector,和主线程后续的push_back操作产生数据竞争:

  • std::vector采用连续内存存储元素,当push_back检测到当前剩余容量不足时,会申请一块更大的新内存,把已有元素拷贝/移动到新内存后释放旧内存。
  • 代码中每插入一个元素就立刻创建对应工作线程,线程创建成功后会马上进入worker函数读取gTestVec[i]的元素地址执行append操作;此时主线程还在继续循环执行后续的push_back,一旦触发vector扩容,之前已经启动的工作线程持有的旧内存地址就变成野指针,后续对野指针指向的string做append、reserve操作就会触发段错误,和gdb栈中崩在string::reserve/string::append的特征完全吻合。
  • 哪怕提前给vector reserve了足够容量,push_back修改vector size成员和工作线程读取vector元素的操作没有同步,依然属于C++标准定义的数据竞争,属于未定义行为,仍可能出现偶发崩溃。

注意:“不同线程操作不同索引元素就无冲突”的假设,成立前提是vector的底层存储结构已经完全固定,不会再发生扩容、元素内存地址变动的情况。

修复方案
  • 把vector初始化和线程创建逻辑拆成两个独立循环:先循环完成所有push_back操作,等vector的大小、元素内存地址完全固定后,再循环创建所有工作线程,从根源上避免主线程修改vector结构和工作线程访问vector的并发冲突。
    修复后的核心逻辑示例:
    void do_complete_stage_test::excute() {
      int i = 0;
      pthread_t thd[NUM];
      thread_data data[NUM];
    
      // 第一步:单线程完成所有vector元素插入,期间无其他线程访问vector
      for (i=0; i<NUM; i++) {
        gTestVec.push_back(format("%d", i));
        data[i].thread_id = i;
      }
    
      // 第二步:vector结构固定后,统一创建工作线程
      for (i=0; i<NUM; i++) {
        if (0 != pthread_create(&(thd[i]), NULL, &worker, (void *)&data[i])) {
          printf("pthread_create failed");
        }
      }
    
      for (int i=0; i<NUM; i++) {
        if (0 != pthread_join(thd[i], NULL)) {
          printf("pthread_join failed");
        }
      }
    }
    
  • 如果业务场景必须在线程运行过程中动态增删vector元素,必须加互斥锁保护所有对vector的读写操作,不能直接无锁并发访问。

内容的提问来源于stack exchange,提问作者zilliax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 12:54:18