C++ 学习(10)——多线程题目
1. 线程安全的计数器(Lock-based)
题目描述
创建 10 个线程,每个线程对同一个全局计数器累加 100 万次。最后输出计数器的值。
要求
- 计数器类型为
std::uint64_t。 - 使用互斥量(
std::mutex或std::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_semaphore 或 std::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::jthread和std::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_lock,put使用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→6 做,每完成一题就阅读一次 C++ 标准库对应章节(
、 、<condition_variable>、 、<shared_mutex>、 )。 - 用 Sanitizer(-fsanitize=thread)跑一遍,确保无数据竞争。
- 把每题做成可编译的独立小项目,用 Git 管理,方便回顾。
- 进阶时尝试 C++20/23 新特性:
std::jthread、std::barrier、std::latch、std::counting_semaphore、std::stop_token、std::atomic_ref、std::syncbuf。

浙公网安备 33010602011771号