本实验不属于考试内容,在此简单略过,只作了解
主线项目:MiniEngine — 子系统 B 收尾 + 全工程 demo
在这里插入图片描述
在这里插入图片描述
看到这里我真的有点懵,所以为了节约时间咱们尽快做完

Task 1:导入骨架,确认工程前置条件

在这里插入图片描述
新增文件:
include/ThreadPool.h ← 骨架(填写 TODO)
include/Scheduler.h ← 骨架(填写 TODO)
apps/week08/thread_pool_lab.cpp ← 练习(填写 TODO)
apps/week08/main.cpp ← 只读(端到端 demo)
tests/ThreadPoolTest.cpp ← 已有测试 + 末尾 TODO 追加区
tests/SchedulerTest.cpp ← 已有测试 + 末尾 TODO 追加区

完成之后先尝试能不能构建成功

cmake -S . -B build -G "MinGW Makefiles"
cmake --build build --target week08_thread_pool_lab

能够构建(即使 TODO 未填写),说明工程配置正确,可以继续完成热身练习。

git checkout main
git checkout -b lab/week08-threadpool
git add .
git commit -m "init: 导入 MiniEngine 第8周骨架"

注意。这里是不需要push的

Task 2:线程池热身练习

1 要求

在这里插入图片描述
在这里插入图片描述

2 代码

#include <chrono>
#include <condition_variable>
#include <exception>
#include <functional>
#include <future>
#include <iostream>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>

// ─────────────────────────────────────────────────────────────────────────────
// 第 8 周线程池热身练习
//
// 运行方式:
//   cmake --build build --target week08_thread_pool_lab
//   ./build/apps/week08_thread_pool_lab
//
// 四道练习逐步引导:
//   1. 条件变量 + 谓词 wait
//   2. 生产者-消费者最小模型
//   3. packaged_task + future 取值
//   4. 异常通过 future 传播
//   5. 迷你池(桥接到 Task 3):把 1-4 组装为"1 个 worker 的最小池"
// ─────────────────────────────────────────────────────────────────────────────

// ── Exercise 1:cv.wait 的谓词写法 ────────────────────────────────────────────
//
// 要求:
//   1. 主线程创建 worker 线程,worker 通过 cv.wait(lock, pred) 等待 ready == true
//   2. 主线程休眠 100ms 后加锁设置 ready=true、value=42,然后 cv.notify_one()
//   3. worker 收到通知后打印 "worker awakened, value = 42"
//
// 预期输出:
//   === Exercise 1: cv.wait with predicate ===
//   worker awakened, value = 42

void Exercise1() {
    std::cout << "\n=== Exercise 1: cv.wait with predicate ===\n";

    std::mutex mtx;
    std::condition_variable cv;
    bool ready = false;
    int value = 0;

    std::thread worker([&]{
        std::unique_lock<std::mutex> lock(mtx);
        // TODO: 使用带谓词的 cv.wait,等待 ready 变为 true
        //   cv.wait(lock, [&]{ return ready; });
        // 然后打印 "worker awakened, value = " << value
        cv.wait(lock, [&]{ return ready; });
        std::cout << "worker awakened, value = " << value << "\n";
    });

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // TODO: 加锁 → 设置 ready=true, value=42 → 解锁 → cv.notify_one()
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        value = 42;
    }           // 先解锁再 notify,避免 worker 被唤醒后立刻重新阻塞在锁上
    cv.notify_one();

    worker.join();
}

// ── Exercise 2:生产者-消费者最小模型 ──────────────────────────────────────────
//
// 要求:
//   1. 一个生产者线程依次将 1..5 推入队列(每次 push 后 notify_one)
//   2. 一个消费者线程等待队列非空,出队并打印
//   3. 生产者生产完后,用一个 done 标志通知消费者退出
//
// 预期输出:
//   === Exercise 2: Producer-Consumer ===
//   consumed: 1 2 3 4 5

void Exercise2() {
    std::cout << "\n=== Exercise 2: Producer-Consumer ===\n";

    std::mutex mtx;
    std::condition_variable cv;
    std::queue<int> q;
    bool done = false;

    std::thread producer([&]{
        for (int i = 1; i <= 5; ++i) {
            // TODO: 加锁 → q.push(i) → 解锁 → cv.notify_one()
            {
                std::lock_guard<std::mutex> lock(mtx);
                q.push(i);
            }
            cv.notify_one();
        }
        // TODO: 加锁 → done=true → 解锁 → cv.notify_all()
        {
            std::lock_guard<std::mutex> lock(mtx);
            done = true;
        }
        cv.notify_all();// 告知消费者"不再有新数据"
    });

    std::thread consumer([&]{
        std::cout << "consumed:";
        while (true) {
            std::unique_lock<std::mutex> lock(mtx);
            // TODO: cv.wait(lock, [&]{ return !q.empty() || done; });
            // TODO: 如果 q 空且 done 为 true,break
            // TODO: 取出队头元素 v 并 pop,解锁后打印 " " << v
             cv.wait(lock, [&]{ return !q.empty() || done; });
             if (q.empty() && done)
                 break;// 队列空且生产结束,退出循环
            
            int v = q.front();
            q.pop();
            lock.unlock();
            std::cout << " " << v;   // I/O 前先解锁,不占用锁做输出
            // 占位 break,避免死循环;填好 TODO 后会被上面的逻辑覆盖
            break;
        }
        std::cout << "\n";
    });

    producer.join();
    consumer.join();
}

// ── Exercise 3:packaged_task + future 基础 ──────────────────────────────────
//
// 要求:
//   1. 用 std::packaged_task<int()> 包装一个返回 100 的 lambda
//   2. 从 task 取出 future
//   3. 在独立线程中执行 task
//   4. 主线程 fut.get() 并打印 "result = 100"
//
// 预期输出:
//   === Exercise 3: packaged_task + future ===
//   result = 100

void Exercise3() {
    std::cout << "\n=== Exercise 3: packaged_task + future ===\n";

    // TODO: std::packaged_task<int()> task([]{ return 100; });
    // TODO: auto fut = task.get_future();
    // TODO: std::thread t(std::move(task));
    //       t.join();
    // TODO: std::cout << "result = " << fut.get() << "\n";
    std::packaged_task<int()> task([]{ return 100; }); 
    auto fut = task.get_future();

    std::thread t(std::move(task));
    t.join();

    std::cout << "result = " << fut.get() << "\n";
}

// ── Exercise 4:异常通过 future 传播 ──────────────────────────────────────────
//
// 要求:
//   1. 用 std::async(std::launch::async, ...) 启动一个抛 std::runtime_error("simulated failure") 的任务
//   2. 主线程用 try/catch 包裹 fut.get(),捕获后打印 "caught: simulated failure"
//
// 预期输出:
//   === Exercise 4: Exception propagation ===
//   caught: simulated failure

void Exercise4() {
    std::cout << "\n=== Exercise 4: Exception propagation ===\n";

    // TODO: auto fut = std::async(std::launch::async, []() -> int {
    //           throw std::runtime_error("simulated failure");
    //       });
    auto fut = std::async(std::launch::async, []() -> int {
        throw std::runtime_error("simulated failure");
        return 0;
    });
    // TODO: try { fut.get(); } catch (const std::exception& e) {
    //           std::cout << "caught: " << e.what() << "\n";
    //       }
    try {
        fut.get();
    } catch (const std::exception& e) {
        std::cout << "caught: " << e.what() << "\n";
    }
}

// ── Exercise 5:迷你池(桥接到 Task 3 的 ThreadPool)──────────────────────────
//
// 目标:把前 4 题的元素组装为**只有 1 个 worker 的最小池**,它长什么样?
//   - 一个队列:存 std::function<void()>
//   - 一个 worker 线程:谓词 wait + 出队执行 + 关停时退出
//   - 一个 Submit:用 packaged_task 包装 + 取 future + 入队 + notify_one
//
// 完成后你会意识到:Task 3 的 ThreadPool 无非是把"1 个 worker → N 个 worker"。
//
// 要求:
//   1. 提交 3 个计算任务:平方(1)、平方(2)、平方(3)
//   2. 依次 get() 结果,按 "mini-pool result: [1, 4, 9]" 格式输出
//   3. main 返回前 worker 正确停机(join 前记得 notify_all)
//
// 预期输出:
//   === Exercise 5: Mini pool ===
//   mini-pool result: [1, 4, 9]

void Exercise5() {
    std::cout << "\n=== Exercise 5: Mini pool ===\n";

    std::mutex mtx;
    std::condition_variable cv;
    std::queue<std::function<void()>> tasks;
    bool stop = false;

    // TODO 1: 启动一个 worker 线程,行为与 Task 3 的 WorkerLoop 一致(但只有 1 个)
    // std::thread worker([&]{
    //     while (true) {
    //         std::function<void()> job;
    //         {
    //             std::unique_lock<std::mutex> lk(mtx);
    //             cv.wait(lk, [&]{ return stop || !tasks.empty(); });
    //             if (stop && tasks.empty()) return;
    //             job = std::move(tasks.front());
    //             tasks.pop();
    //         }
    //         job();
    //     }
    // });
    std::thread worker([&]{
        while (true) {
            std::function<void()> job;
            {
                std::unique_lock<std::mutex> lk(mtx);
                cv.wait(lk, [&]{ return stop || !tasks.empty(); });
                if (stop && tasks.empty()) return;  // 停机且无剩余任务
                job = std::move(tasks.front());
                tasks.pop();
            }
            job();  // 锁外执行,不阻塞提交者
        }
    });

    // TODO 2: 写一个本地 lambda submit(fn) -> future<int>,内部:
    //   - 用 shared_ptr<packaged_task<int()>> 包装 fn
    //   - 取出 future
    //   - 加锁入队 lambda [task]{ (*task)(); }
    //   - notify_one()
    //   - 返回 future
    auto submit = [&](std::function<int()> fn) -> std::future<int> {
        std::shared_ptr<std::packaged_task<int()>> task = std::make_shared<std::packaged_task<int()>>(fn);
       std::future<int> fut = task->get_future();
        {
            std::lock_guard<std::mutex> lk(mtx);
            tasks.push([task]{ (*task)(); });   // 入队一个无参 void 包装
        }
        cv.notify_one();
        return fut;
    };

    // TODO 3: 提交三个任务:平方(1)、平方(2)、平方(3)
    //         收集 future 到 vector,依次 get() 并打印:
    //         std::cout << "mini-pool result: [" << a << ", " << b << ", " << c << "]\n";
    auto fut1 = submit([]() { return 1 * 1; });
    auto fut2 = submit([]() { return 2 * 2; });
    auto fut3 = submit([]() { return 3 * 3; });
    std::cout << "mini-pool result: [" << fut1.get() << ", " << fut2.get() << ", " << fut3.get() << "]\n";


    // TODO 4: 停机:加锁置 stop=true → notify_all → worker.join()
    {
        std::lock_guard<std::mutex> lk(mtx);
        stop = true;
    }
    cv.notify_all();
    worker.join();
}

int main() {
    Exercise1();
    Exercise2();
    Exercise3();
    Exercise4();
    Exercise5();
    std::cout << "\nAll exercises done.\n";
    return 0;
}

3 解释

4 输出

cmake --build build --target week08_thread_pool_lab
./build/apps/week08_thread_pool_lab

在这里插入图片描述

输出没问题就提交一下

git add apps/week08/thread_pool_lab.cpp
git commit -m "feat: 完成线程池热身练习(含 Exercise 5 迷你池桥接)"

Task 3:实现 ThreadPool

1 要求

在这里插入图片描述

2 代码

#pragma once

#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>

// ─────────────────────────────────────────────────────────────────────────────
// ThreadPool — 最小正确线程池
//
// 四件套:
//   1. 工作线程集合 workers_:固定数量,提前创建
//   2. 任务队列 tasks_:std::queue<std::function<void()>>
//   3. 同步原语 mtx_ + cv_:保护队列,唤醒 worker
//   4. 生命周期信号 stop_:配合析构函数实现优雅停机
//
// 骨架说明:
//   - 数据成员与公开方法签名已固定,禁止修改
//   - 在下方"实现区"的 TODO 中填写方法体
//
// 铁律:
//   - 锁只保护队列操作,禁止持锁执行任务
//   - 析构三部曲:置位 → notify_all → join,禁止持锁 join
// ─────────────────────────────────────────────────────────────────────────────

class ThreadPool {
public:
    // 构造 n 个工作线程;n == 0 时抛 std::invalid_argument("pool size must be > 0")
    explicit ThreadPool(std::size_t n);

    // 析构:优雅停机(置位 + notify_all + join)
    ~ThreadPool();

    // 禁止拷贝与移动(线程池管理独占的系统资源)
    ThreadPool(const ThreadPool&)            = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;
    ThreadPool(ThreadPool&&)                 = delete;
    ThreadPool& operator=(ThreadPool&&)      = delete;

    // 提交可调用对象 f,返回 future<R>(R = invoke_result_t<F>)
    // 若线程池已停止,抛 std::runtime_error("Submit on stopped pool")
    // 任务抛出的异常通过 future.get() 处重抛
    template <class F>
    auto Submit(F&& f) -> std::future<std::invoke_result_t<F>>;

    // 返回线程池中工作线程的数量
    std::size_t Size() const noexcept { return workers_.size(); }

private:
    // 工作线程主循环
    void WorkerLoop();

    std::vector<std::thread>           workers_;
    std::queue<std::function<void()>>  tasks_;
    mutable std::mutex                 mtx_;
    std::condition_variable            cv_;
    bool                               stop_ = false;
};

// ─────────────────────────────────────────────────────────────────────────────
// 实现区 — 在每个 TODO 中填入方法体,不得修改方法签名
// ─────────────────────────────────────────────────────────────────────────────

inline ThreadPool::ThreadPool(std::size_t n) {
    // TODO:
    // 1. 若 n == 0,抛 std::invalid_argument("pool size must be > 0")
    // 2. 用 workers_.reserve(n) 预分配
    // 3. 循环 n 次,用 workers_.emplace_back([this]{ WorkerLoop(); }) 启动线程
    if (n == 0) {
        throw std::invalid_argument("pool size must be > 0");
    }
    workers_.reserve(n);
    for (std::size_t i = 0; i < n; ++i) {
        workers_.emplace_back([this]{ WorkerLoop(); });
    }
}

inline ThreadPool::~ThreadPool() {
    // TODO(析构三部曲):
    // 1. 加锁并置 stop_ = true(用 lock_guard 限定作用域,离开作用域自动解锁)
    // 2. 解锁后调用 cv_.notify_all() 唤醒所有 worker
    // 3. 遍历 workers_,对 joinable() 的线程逐一 join
    //
    // 注意:join 必须在 mutex 解锁之后!
    // 错误示范:lock_guard g(mtx_); for (auto& t : workers_) t.join();
    //          → 死锁,worker 拿不到 mtx_ 退出不了
    {
        std::lock_guard<std::mutex> g(mtx_);
        stop_ = true;
    }
    cv_.notify_all();
    for (auto& t : workers_) {
        if (t.joinable()) {
            t.join();
        }
    }

}

inline void ThreadPool::WorkerLoop() {
    // TODO(worker 主循环):
    // while (true) {
    //     std::function<void()> task;
    //     {
    //         std::unique_lock<std::mutex> lock(mtx_);
    //         cv_.wait(lock, [&]{ return stop_ || !tasks_.empty(); });
    //         if (stop_ && tasks_.empty()) return;
    //         task = std::move(tasks_.front());
    //         tasks_.pop();
    //     }  // 离开作用域时解锁
    //     task();  // !执行任务时不持有锁
    // }
    while (true) { 
        std::function<void()> task;
        {
            std::unique_lock<std::mutex> lock(mtx_);
            cv_.wait(lock, [&]{ return stop_ || !tasks_.empty(); });
            if (stop_ && tasks_.empty()) return;  // 停机且无剩余任务,退出线程
            task = std::move(tasks_.front());
            tasks_.pop();
        }  // 离开作用域时解锁
        task();  // !执行任务时不持有锁   
    }
}

template <class F>
auto ThreadPool::Submit(F&& f) -> std::future<std::invoke_result_t<F>> {
    using R = std::invoke_result_t<F>;

    // TODO:
    // 1. 用 auto task = std::make_shared<std::packaged_task<R()>>(std::forward<F>(f));
    //    包装任务——packaged_task 会自动把异常记录到关联的 future
    // 2. 用 std::future<R> fut = task->get_future(); 取出 future
    // 3. 加锁检查 stop_,若 true 则抛 std::runtime_error("Submit on stopped pool")
    // 4. 将 [task]{ (*task)(); } 入队(注意:std::function 要求可拷贝,
    //    而 packaged_task 不可拷贝,所以用 shared_ptr 包装)
    // 5. 解锁后 cv_.notify_one() 唤醒一个 worker
    // 6. 返回 fut
    //
    // 提示:这是本周最核心的一段代码,务必对照 PPT 的"任务提交"一页反复阅读
    auto task = std::make_shared<std::packaged_task<R()>>(std::forward<F>(f));
    std::future<R> fut = task->get_future();
    {
        std::lock_guard<std::mutex> lock(mtx_);
        if (stop_) {
            throw std::runtime_error("Submit on stopped pool");
        }
        tasks_.emplace([task]{ (*task)(); });
    }
    cv_.notify_one();
    return fut;  // TODO: 返回真正的 future
}

3 解释

4 输出

cmake --build build --target ThreadPoolTest
./build/tests/ThreadPoolTest

在这里插入图片描述

所有已有测试(建议 ≥ 10 个)须通过后,进入 Task 4。

git add include/ThreadPool.h
git commit -m "feat(threadpool): 实现ThreadPool"

MR1

git push -u origin lab/week08-threadpool

[Week08] 实现 ThreadPool + 热身练习

Task 4:TaskGraph::ExecuteOnPool

1 要求

在这里插入图片描述

2 代码

// ─────────────────────────────────────────────────────────────────────────────
// 【本周 TODO】ExecuteOnPool — 把任务提交到线程池
// ─────────────────────────────────────────────────────────────────────────────

inline std::vector<std::future<void>> TaskGraph::ExecuteOnPool(ThreadPool& pool) {
    // TODO:
    // 1. 调用 TopologicalSort() 获得执行序 order(若有环会抛 runtime_error,直接上抛给调用方)
    // 2. 对每个 id,调用 pool.Submit(nodes_.at(id)->work),把返回的 future 存入结果 vector
    // 3. 注意:不要在方法内部调用 future.get(),由调用方负责等待
    //
    // 提示:pool.Submit(...) 返回 std::future<void>,可以直接 push_back
    auto order = TopologicalSort();
    std::vector<std::future<void>> futures;
    futures.reserve(order.size());
    for (int id : order) {
        futures.push_back(pool.Submit(nodes_.at(id)->work));
    }
    return futures;   // TODO: 返回真正的 futures
}

3 解释

4 输出

git checkout main
git checkout -b lab/week08-integration
git add include/TaskGraph.h
git commit -m "feat(taskgraph):实现 TaskGraph的ExecuteOnPool新方法 "

Task 5:调度策略(策略模式 × std::function)

1 要求

在这里插入图片描述

2 代码

#pragma once

#include <algorithm>
#include <functional>
#include <stdexcept>
#include <unordered_map>
#include <vector>

// ─────────────────────────────────────────────────────────────────────────────
// Scheduler — 任务调度策略(策略模式现代 C++ 写法)
//
// 核心思路:
//   用 std::function 替代继承体系持有"选哪个节点"的策略。
//   策略签名:int(const std::vector<int>&) ——
//   接受当前就绪节点 ID 列表,返回本轮应派发的节点 ID。
//
// 骨架说明:
//   - PickFn 类型别名已固定,禁止修改
//   - Scheduler 的数据成员已固定,禁止修改
//   - 在下方每个 TODO 中填写方法体
//
// 铁律:
//   - SetPolicy / Pick / MakeFIFO / MakePriority 共四处 TODO,逐一完成
//   - 不得修改 PickFn 定义与 Scheduler 的 public 方法签名
// ─────────────────────────────────────────────────────────────────────────────

// 调度策略类型:接受就绪节点 ID 列表,返回选中的节点 ID
using PickFn = std::function<int(const std::vector<int>&)>;

class Scheduler {
public:
    // 注入调度策略;若传入空 PickFn,Pick() 将回退到默认 FIFO 行为
    void SetPolicy(PickFn f) {
        // TODO: pick_ = std::move(f);
        pick_ = std::move(f);
    }

    // 从就绪节点列表中按当前策略选出一个节点 ID
    //   - ready 为空时抛 std::runtime_error("no ready nodes")
    //   - 未调用 SetPolicy 或策略为空时,默认返回 ready.front()(FIFO)
    //   - 已设置策略时,调用 pick_(ready) 并返回其结果
    int Pick(const std::vector<int>& ready) const {
        // TODO
        if (ready.empty()) {
            throw std::runtime_error("no ready nodes");
        }
        if (!pick_)
            return ready.front();
 
        return pick_(ready);
    }

    // ── 内置策略工厂 ──────────────────────────────────────────────────────────

    // MakeFIFO:始终取就绪列表的第一个(先入先出,与 Kahn 算法默认顺序一致)
    static PickFn MakeFIFO() {
        // TODO: 返回 [](const std::vector<int>& r){ return r.front(); }
        return [](const std::vector<int>& r){ 
            return r.front(); 
        };
    }

    // MakePriority:按优先级表选取优先级最高(值最大)的节点 ID
    //   priority_map: 节点 ID → 优先级整数(未出现的 ID 优先级视为 0)
    //
    // 实现提示:
    //   return [pm = std::move(priority_map)](const std::vector<int>& r) {
    //       return *std::max_element(r.begin(), r.end(),
    //           [&](int a, int b) {
    //               auto get = [&](int id) {
    //                   auto it = pm.find(id);
    //                   return it != pm.end() ? it->second : 0;
    //               };
    //               return get(a) < get(b);
    //           });
    //   };
    static PickFn MakePriority(std::unordered_map<int, int> priority_map) {
         return [pm = std::move(priority_map)](const std::vector<int>& r) {
            // max_element 以"优先级值"为比较键
            return *std::max_element(r.begin(), r.end(),
                [&](int a, int b) {
                    auto get = [&](int id) {
                        auto it = pm.find(id);
                        return it != pm.end() ? it->second : 0;
                    };
                    return get(a) < get(b);  // a < b → a 优先级更低
                });
        };
    }

private:
    PickFn pick_;
};

3 解释

4 输出

验证

cmake --build build --target SchedulerTest
./build/tests/SchedulerTest

在这里插入图片描述

6 个基础测试全部通过后,继续完成 Task 6。
提交

git add include/Scheduler.h tests/SchedulerTest.cpp
git commit -m "feat: 实现 Scheduler 调度策略(FIFO + Priority)"

Task 6:端到端 demo + 测试通过

Step A:补充学生测试

在这里插入图片描述
代码

ThreadPoolTest.cpp

/ TODO: 在此追加至少 3 个学生测试
TEST(ThreadPool, StudentTest_ThousandIncrements) {
    constexpr int kTasks = 1000;
    ThreadPool pool(8);

    std::atomic<int> counter{0};
    std::vector<std::future<void>> futs;

    for (int i = 0; i < kTasks; ++i) {
        futs.push_back(pool.Submit([&]{
            counter.fetch_add(1);
        }));
    }

    for (auto& f : futs) f.get();
    EXPECT_EQ(counter.load(), kTasks);
}

TEST(ThreadPool, StudentTest_MixedReturnTypes) {
    ThreadPool pool(4);

    auto f1 = pool.Submit([]{ return 10; });
    auto f2 = pool.Submit([]{ return std::string("hello"); });
    auto f3 = pool.Submit([]{});

    EXPECT_EQ(f1.get(), 10);
    EXPECT_EQ(f2.get(), "hello");
    f3.get(); // void task
}

TEST(ThreadPool, StudentTest_HighThroughputManyShortTasks) {
    constexpr int kTasks = 5000;   // 压力更大一点
    ThreadPool pool(8);

    std::atomic<int> counter{0};
    std::vector<std::future<void>> futs;
    futs.reserve(kTasks);

    for (int i = 0; i < kTasks; ++i) {
        futs.emplace_back(pool.Submit([&]{
            // 极短任务(几乎无计算)
            counter.fetch_add(1, std::memory_order_relaxed);
        }));
    }

    for (auto& f : futs) {
        f.get();
    }

    EXPECT_EQ(counter.load(), kTasks);
}

SchedulerTest.cpp

// TODO: 在此追加至少 2 个学生测试
TEST(Scheduler, StudentTest_FIFOSingleElement) {
    Scheduler sched;
    sched.SetPolicy(Scheduler::MakeFIFO());
    std::vector<int> ready = {42};
    EXPECT_EQ(sched.Pick(ready), 42);
}

TEST(Scheduler, StudentTest_PickNoSideEffect) {
    Scheduler sched;
    sched.SetPolicy(Scheduler::MakeFIFO());

    std::vector<int> r1 = {1, 2, 3};
    std::vector<int> r2 = {4, 5};

    EXPECT_EQ(sched.Pick(r1), 1);
    EXPECT_EQ(sched.Pick(r2), 4);  // 不应受上次调用影响
}

TaskGraphTest.cpp

// TODO: 追加学生测试
TEST(TaskGraph, StudentTest_ExecuteAsyncDiamondGraph) {
    TaskGraph g;
    std::atomic<int> count{0};

    g.AddNode(1, "A", [&]{ count.fetch_add(1); });
    g.AddNode(2, "B", [&]{ count.fetch_add(1); });
    g.AddNode(3, "C", [&]{ count.fetch_add(1); });
    g.AddNode(4, "D", [&]{ count.fetch_add(1); });

    // diamond: 1→2, 1→3, 2→4, 3→4
    g.AddEdge(1, 2);
    g.AddEdge(1, 3);
    g.AddEdge(2, 4);
    g.AddEdge(3, 4);

    auto futures = g.ExecuteAsync();
    for (auto& f : futures) f.get();

    EXPECT_EQ(count.load(), 4);
}

TEST(TaskGraph, StudentTest_ExecuteThrowsOnCycle) {
    TaskGraph g;
    g.AddNode(1, "A", [](){});
    g.AddNode(2, "B", [](){});

    g.AddEdge(1, 2);
    g.AddEdge(2, 1);  // cycle

    EXPECT_THROW(g.Execute(), std::runtime_error);
}

测试

cmake --build build --target ThreadPoolTest
./build/tests/ThreadPoolTest

10+3=13
在这里插入图片描述

cmake --build build --target SchedulerTest
./build/tests/SchedulerTest

6+2=8
在这里插入图片描述

cmake --build build --target TaskGraphTest
./build/tests/TaskGraphTest

10+2=12
在这里插入图片描述

Step B:运行所有测试

cmake --build build
ctest --test-dir build --output-on-failure

在这里插入图片描述

Step C:端到端 demo 行为验证

在这里插入图片描述

cmake --build build --target week08_app
./build/apps/week08_app

在这里插入图片描述

完成上面所有

git add include/ThreadPool.h include/TaskGraph.h \
        include/Scheduler.h \
        tests/ThreadPoolTest.cpp tests/SchedulerTest.cpp 
git commit -m "feat: 实现 ThreadPool,接入 TaskGraph,调度策略,完成端到端 demo"

Task 7:课程收尾反思

课程收尾反思(必做,作为 Task 7 验收项之一)
本周是 8 周课程的最后一个实验。请在项目 README.md 末尾新增一节
课程收尾反思,回答以下两个问题(总计 ≤ 1 页):

范式选型回顾:demo 中哪些地方用了 OOP?哪些地方用了泛型?哪些地方用了函数式(Lambda / std::function / std::visit)?为什么?请引用你自己代码的文件:行号。
一个反例:如果让你用传统继承式 Visitor 重写 demo 中的某个分发点(例如事件订阅、任务提交),代码会膨胀多少?请贴出至少一个前后对比片段。

git add README.md
git commit -m "docs(readme): 补充课程收尾反思(Task 7 验收)"

MR2

git push -u origin lab/week08-integration

发起 MR 2,描述中附 ctest 总输出截图 + week08_app 输出截图。
[Week08] 接入 TaskGraph、调度策略、与端到端 demo

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐