多线程调用C++ std::list.remove()触发段错误该如何解决?
问题根因
STL标准容器默认不具备线程安全性,多个线程同时对同一个std::list执行读写、写操作时,会破坏容器内部的链表节点结构。
你遇到的段错误完全符合该场景:从栈回溯可以看到,程序调用std::list::size()时需要遍历全表计算长度(C++11之前的std::list::size是O(n)复杂度),此时另一个线程正在执行remove操作删除节点,遍历过程中访问到已经被释放的节点内存,触发访问越界。
可行解决方案
方案1:加互斥锁保护所有容器操作(最稳妥、兼容性最高)
所有对table的访问(包括读操作如size()、遍历,写操作如push_back、remove)都必须加同一把互斥锁,保证同一时间只有一个线程操作容器。
C++11及以上版本示例代码:
#include <mutex> #include <list> std::list<intptr_t> table; // 全局互斥锁,专门用于保护table的所有操作 std::mutex table_mtx; void * customer(void* vargp){ intptr_t tid = (intptr_t)vargp; // 顾客就座,加锁操作容器 { std::lock_guard<std::mutex> lock(table_mtx); table.push_back(tid); } // 代码块结束自动解锁,不阻塞后续业务逻辑的并行执行 // 餐桌处业务逻辑,无需加锁,可并行执行 // do actions at table // 顾客离开,加锁操作容器 { std::lock_guard<std::mutex> lock(table_mtx); table.remove(tid); } }
如果是C++03环境,可替换为pthread_mutex_t实现互斥锁,逻辑完全一致。
注意:其他代码中如果有调用
table.size()、遍历table的逻辑,也必须加同一把锁,否则仍然会出现段错误。
方案2:优化删除效率(可选)
当前list::remove(tid)的复杂度是O(n),每次删除都要遍历全表。如果并发线程数较多可以优化:就座时在加锁状态下记录插入节点的迭代器,离开时直接通过迭代器调用erase删除,复杂度降为O(1)。
额外验证点
如果加锁后仍然出现段错误,可检查:
- 是否有遗漏的未加锁的容器访问操作
- 互斥锁的作用域是否正确,是否出现提前解锁、重复加锁的问题
内容的提问来源于stack exchange,提问作者GildedTruth
相关产品推荐
相关产品推荐

