C++ 学习(10)——多线程题目

1. 线程安全的计数器(Lock-based)

题目描述
创建 10 个线程,每个线程对同一个全局计数器累加 100 万次。最后输出计数器的值。

要求

  • 计数器类型为 std::uint64_t
  • 使用互斥量(std::mutexstd::lock_guard/std::unique_lock)保证线程安全。
  • 运行结束时必须严格输出 10000000

提示

  • 注意 ++cnt 并非原子操作。
  • 可以对比加锁与不加锁的运行结果差异。

进阶思考
改用 std::atomic<std::uint64_t> 实现无锁版本,比较两种方案的性能。


略。

2. 生产者–消费者模型(条件变量)

题目描述
实现一个多生产者、多消费者程序:

  • 生产者线程随机生成整数并放入缓冲区;
  • 消费者线程从缓冲区取出整数并累加求和;
  • 当缓冲区满时生产者阻塞,当缓冲区空时消费者阻塞。

要求

  • 使用 std::mutex + std::condition_variable 同步。
  • 缓冲区容量、生产者数量、消费者数量都可在程序启动时通过命令行参数指定。
  • 程序结束时输出所有消费者累加的总和。

提示

  • 设计一个循环队列或 std::queue 作为缓冲区。
  • 使用谓词 lambda 防止虚假唤醒。

进阶思考
尝试用 C++20 的 std::counting_semaphorestd::barrier 改写。


3. 线程池(Task-based)

题目描述
实现一个固定大小线程池(Thread Pool)类 ThreadPool,支持如下接口:

class ThreadPool {
public:
    explicit ThreadPool(std::size_t n);
    template<class F>
    auto enqueue(F&& f) -> std::future<decltype(f())>;
    ~ThreadPool();
};

要求

  • 线程池构造函数创建 n 个工作线程。
  • enqueue 接受任意可调用对象,返回 std::future,调用者可通过 future 获取结果。
  • 析构函数优雅地停止所有线程(完成剩余任务后退出)。

提示

  • 使用 std::queue<std::function<void()>> 存储任务;
  • 使用 std::mutex + std::condition_variable 管理任务队列;
  • std::packaged_task + std::future 实现返回值传递。

进阶思考

  • 支持动态扩容/缩容线程数;
  • 使用 C++20 std::jthreadstd::stop_token 简化代码。

4. 读写锁(shared_mutex)

题目描述
实现一个线程安全的缓存类 KVCache,支持并发读、独占写:

class KVCache {
public:
    std::optional<std::string> get(const std::string& key);
    void put(const std::string& key, const std::string& value);
};

要求

  • 读操作允许多线程并发;写操作必须独占。
  • 使用 std::shared_mutex(C++17)或 std::shared_timed_mutex(C++14)。
  • 写操作完成后,所有读线程应立即可见新值。

提示

  • get 使用 std::shared_lockput 使用 std::unique_lock
  • std::unordered_map 存储键值对。

进阶思考
实现 LRU 淘汰策略,同时保持读写锁的并发度。


5. 无锁并发栈(Lock-free)

题目描述
基于链表实现一个无锁(lock-free)并发栈 LockFreeStack<T>

template<typename T>
class LockFreeStack {
public:
    void push(const T& value);
    bool pop(T& out);   // 返回 false 表示栈空
};

要求

  • 使用 std::atomic<Node*> 实现无锁链表头指针。
  • 采用 C++11 原子操作(compare_exchange_strong/weak)。
  • 通过 ABA 检测(Hazard Pointer 或 epoch-based reclamation)解决内存回收问题。

提示

  • 先实现一个简化版:只允许单生产者单消费者,不加 ABA 处理。
  • 再尝试支持多生产者多消费者,并解决 ABA 问题。

进阶思考
用 C++20 的 std::atomic<std::shared_ptr<T>> 简化内存管理。


6. 并行 MapReduce(高级综合)

题目描述
实现一个简化版 MapReduce 框架,接口如下:

template<typename In, typename K, typename V>
class MapReduce {
public:
    using Mapper   = std::function<std::vector<std::pair<K,V>>(const In&)>;
    using Reducer  = std::function<V(const K&, const std::vector<V>&)>;

    MapReduce(std::size_t num_workers);
    std::unordered_map<K,V> run(const std::vector<In>& input,
                                Mapper map,
                                Reducer reduce);
};

要求

  • 自动把输入数据分片,分配给工作线程并行 map。
  • 在 map 阶段后,根据 key 做 shuffle(分区),再并行 reduce。
  • 所有阶段线程安全,不使用全局锁。
  • 返回最终聚合结果。

提示

  • 用线程池执行 map/reduce 任务;
  • std::unordered_map<K, std::vector<V>> 暂存中间结果,注意并发插入;
  • 可以先用 concurrent_queue 或 lock-free 队列做线程间通信。

进阶思考

  • 支持流水线(pipeline)方式让 map 和 reduce 部分重叠;
  • 引入 Combiner 减少网络/内存 shuffle 量。

学习建议

  1. 按顺序从 1→6 做,每完成一题就阅读一次 C++ 标准库对应章节(、<condition_variable>、、<shared_mutex>、)。
  2. 用 Sanitizer(-fsanitize=thread)跑一遍,确保无数据竞争。
  3. 把每题做成可编译的独立小项目,用 Git 管理,方便回顾。
  4. 进阶时尝试 C++20/23 新特性:std::jthreadstd::barrierstd::latchstd::counting_semaphorestd::stop_tokenstd::atomic_refstd::syncbuf
posted @ 2025-07-23 11:28  seekwhale13  阅读(30)  评论(0)    收藏  举报