← 返回 Day 54 正文

Day 54 · 练习与清单

Day 054 — 并发入门:线程 — 练习清单

✅ 今日完成清单


📝 基础练习题

练习 1:多线程累加器

创建一个程序,使用 10 个线程同时累加到一个共享变量,每个线程累加 100,000 次。分别用不加锁和加锁的方式实现,对比结果。

要求: - 不加锁版本结果不稳定 - 加锁版本结果始终正确 - 打印两种方式的对比


练习 2:线程安全的栈

实现一个线程安全的栈类 ThreadSafeStack,支持以下操作: - push(item) — 入栈 - pop() — 出栈(栈空时返回 None) - peek() — 查看栈顶元素 - size() — 获取大小

要求: - 所有操作必须线程安全 - 编写测试代码验证并发安全


练习 3:并行文件处理

编写一个程序,用多线程读取一个目录下的所有 .txt 文件,统计每个文件的行数和总字数。

要求: - 使用 ThreadPoolExecutor - 每个文件单独一个线程处理 - 汇总结果并打印


🚀 进阶挑战题

挑战 1:生产者-消费者带背压

实现一个生产者-消费者模型: - 生产者产生 0-99 的数字 - 消费者计算数字的平方并存储 - 缓冲区最大容量为 5 - 当缓冲区满时,生产者等待 - 当缓冲区空时,消费者等待

进阶要求: - 实现多个生产者和多个消费者 - 统计每个消费者处理了多少个元素


挑战 2:读写锁

实现一个读写锁(Read-Write Lock): - 多个线程可以同时读取 - 写入时不允许其他线程读写 - 写入优先(防止写饥饿)

提示: - 使用 threading.Condition - 维护读取计数器和写入标志


挑战 3:线程池监控器

扩展 ConcurrentDownloader 类,添加以下功能: - 实时显示下载速度(bytes/s) - 支持取消未开始的任务 - 下载失败时自动重试(最多 3 次) - 生成 JSON 格式的下载报告


💡 思考题

  1. 为什么 threading.Thread 的 args 参数必须是元组?args=(1) 和 args=(1,) 有什么区别?

  2. 在什么场景下,多线程程序的性能可能比单线程更差?举出至少 2 个例子。

  3. threading.local() 的作用是什么?它和全局变量有什么区别?在什么场景下使用?