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

C++中其他线程删除元素时,如何安全遍历std::list?

解决方案:多线程遍历并修改std::list的线程安全实现

你的核心问题是在避免全局遍历锁的前提下,解决std::list并发读(遍历)和写(删除)的线程安全问题——原代码中直接并发操作会导致迭代器失效、未定义行为,甚至段错误,因为std::list本身不是线程安全容器,并发读写不存在内置同步。

下面是几种可行的实现方案,兼顾性能和线程安全:

方案1:细粒度互斥锁+提前获取下一个迭代器

利用std::list的特性:删除元素只会让被删元素的迭代器失效,其他迭代器仍有效。通过缩小锁的范围(仅在获取元素、迭代器时加锁),避免全局遍历锁带来的性能损失。

代码实现

#include <mutex>
#include <list>
#include <iostream>
#include <thread>

std::list<Type> items;
std::mutex mtx;

void* Thread1(void*)
{
    while(true) {
        auto it = items.begin();
        while (it != items.end()) {
            // 仅在获取元素副本和下一个迭代器时加锁
            std::lock_guard<std::mutex> lock(mtx);
            if (it == items.end()) break; // 防止刚拿到迭代器就被删到end
            Type item_copy = *it;
            auto next_it = std::next(it); // 提前保存下一个迭代器,避免当前it被erase后失效
            lock.unlock();

            // 无锁处理元素
            std::cout << item_copy << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            // 重新加锁更新迭代器
            std::lock_guard<std::mutex> lock2(mtx);
            it = next_it;
        }
    }
}

void* Thread2(void*)
{
    // 删除操作全程加锁,确保原子性
    std::lock_guard<std::mutex> lock(mtx);
    for(auto item = items.begin(); item != items.end(); ++item) {
        if(/* 你的条件判断 */) {
            items.erase(item);
            break;
        }
    }
}

优缺点

  • 优点:实现简单,无需修改元素结构,删除操作立即生效,锁粒度极小(仅毫秒级的同步操作),对遍历性能影响可以忽略。
  • 缺点:Thread2删除时需要等待Thread1当前的锁释放(但Thread1的锁持有时间极短),极端高并发场景下可能有微小的延迟。

方案2:原子标记+延迟删除

如果对删除的时效性要求不高,可以给元素添加一个待删除的原子标记,Thread1遍历到标记元素时跳过,同时在遍历间隙清理已标记的元素。这种方式读操作几乎无锁,性能最优。

代码实现

#include <atomic>
#include <mutex>
#include <list>
#include <iostream>
#include <thread>

struct Type {
    // 你的原有成员
    std::atomic<bool> to_be_deleted{false}; // 原子标记,线程安全修改
};

std::list<Type> items;
std::mutex mtx;

void* Thread1(void*)
{
    while(true) {
        auto it = items.begin();
        while (it != items.end()) {
            // 无锁检查标记,未标记则处理元素
            if (!it->to_be_deleted.load(std::memory_order_acquire)) {
                std::cout << *it << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }

            // 加锁清理已标记的元素
            std::lock_guard<std::mutex> lock(mtx);
            if (it != items.end() && it->to_be_deleted.load(std::memory_order_acquire)) {
                it = items.erase(it); // erase后自动更新迭代器
            } else {
                ++it;
            }
        }
    }
}

void* Thread2(void*)
{
    std::lock_guard<std::mutex> lock(mtx);
    for(auto item = items.begin(); item != items.end(); ++item) {
        if(/* 你的条件判断 */) {
            item->to_be_deleted.store(true, std::memory_order_release);
            break;
        }
    }
}

优缺点

  • 优点:读操作完全无锁,遍历性能几乎不受影响,适合超大列表的高频遍历场景。
  • 缺点:元素不会被立即删除,需等到Thread1遍历到该元素时才会清理;需要修改元素类型添加原子标记。

方案3:读写锁(std::shared_mutex)

如果未来可能扩展为多个读线程,可使用C++17引入的std::shared_mutex,允许多个读线程同时获取共享锁,写线程独占锁,进一步优化并发性能。

代码实现

#include <shared_mutex>
#include <list>
#include <iostream>
#include <thread>

std::list<Type> items;
std::shared_mutex rw_mtx;

void* Thread1(void*)
{
    while(true) {
        auto it = items.begin();
        while (it != items.end()) {
            // 读操作加共享锁
            std::shared_lock<std::shared_mutex> lock(rw_mtx);
            if (it == items.end()) break;
            Type item_copy = *it;
            auto next_it = std::next(it);
            lock.unlock();

            std::cout << item_copy << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            std::shared_lock<std::shared_mutex> lock2(rw_mtx);
            it = next_it;
        }
    }
}

void* Thread2(void*)
{
    // 写操作加独占锁
    std::unique_lock<std::shared_mutex> lock(rw_mtx);
    for(auto item = items.begin(); item != items.end(); ++item) {
        if(/* 你的条件判断 */) {
            items.erase(item);
            break;
        }
    }
}

优缺点

  • 优点:支持多读单写的高并发场景,锁粒度同样极小,性能表现优异。
  • 缺点:依赖C++17及以上标准,实现复杂度略高于普通互斥锁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:05:58