并发操纵的同步_第四章_《C++并发编程实战》条记

[复制链接]
发表于 2025-10-15 13:55:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1. 期待变乱或期待其他条件

std::condition_variable:是 C++ 尺度库提供的用于线程间同步的工具,它允许一个或多个线程期待某个条件满足后再继承实验。通常与 std::mutex 一起使用,std::mutex 用于掩护共享数据,std::condition_variable 用于线程间的关照机制。
虚伪唤醒 :指线程在没有收到明白关照的情况下从 wait 状态中被唤醒。为克制虚伪唤醒,wait 函数通常会担当一个谓词(可调用对象)作为参数,线程被唤醒后会查抄该谓词的返回值,假如为 false 则会继承期待。
  1. #include <iostream>
  2. #include <thread>
  3. #include <mutex>
  4. #include <condition_variable>
  5. std::mutex mtx;
  6. std::condition_variable cv;
  7. bool ready = false;
  8. void worker() {
  9.     std::unique_lock<std::mutex> lock(mtx);
  10.     // 等待 ready 变为 true
  11.     cv.wait(lock, [] { return ready; });
  12.     std::cout << "Worker thread is working." << std::endl;
  13. }
  14. void prepare_work() {
  15.     {
  16.         std::lock_guard<std::mutex> lock(mtx);
  17.         ready = true;
  18.     }
  19.     // 通知一个等待的线程
  20.     cv.notify_one();
  21.     std::cout << "Work is prepared." << std::endl;
  22. }
  23. int main() {
  24.     std::thread t1(worker);
  25.     std::thread t2(prepare_work);
  26.     t1.join();
  27.     t2.join();
  28.     return 0;
  29. }
  30. /*
  31. 1. worker 线程首先获取 std::unique_lock 锁定互斥量 mtx,然后调用 cv.wait(lock, [] { return ready; })。wait 函数会检查 ready 条件,如果不满足,它会释放锁并使线程进入等待状态。
  32. 2. prepare_work 线程获取锁,将 ready 置为 true,释放锁后调用 cv.notify_one() 通知一个等待的线程。
  33. 3. 当 worker 线程被通知后,它会重新获取锁并继续执行。
  34. */
复制代码
2. 基于条件变量的生产者 - 斲丧者模子



  • 焦点概念
    生产者 - 斲丧者模子是一种常见的并发编程模式,此中生产者线程负责天生数据并将其放入共享缓冲区,斲丧者线程负责从缓冲区中取出数据并举行处置处罚。使用条件变量可以实现生产者和斲丧者之间的同步,克制数据竞争和缓冲区溢出。
  • 代码
  1. #include <iostream>
  2. #include <queue>
  3. #include <thread>
  4. #include <mutex>
  5. #include <condition_variable>
  6. std::queue<int> data_queue;
  7. std::mutex mtx;
  8. std::condition_variable data_cond;
  9. void producer() {
  10.     for (int i = 0; i < 5; ++i) {
  11.         {
  12.             std::lock_guard<std::mutex> lock(mtx);
  13.             data_queue.push(i);
  14.         }
  15.         // 通知消费者有新数据
  16.         data_cond.notify_one();
  17.         std::this_thread::sleep_for(std::chrono::seconds(1));
  18.     }
  19. }
  20. void consumer() {
  21.     while (true) {
  22.         std::unique_lock<std::mutex> lock(mtx);
  23.         // 等待队列中有数据
  24.         data_cond.wait(lock, [] { return!data_queue.empty(); });
  25.         int value = data_queue.front();
  26.         data_queue.pop();
  27.         lock.unlock();
  28.         std::cout << "Consumed: " << value << std::endl;
  29.     }
  30. }
  31. int main() {
  32.     std::thread t1(producer);
  33.     std::thread t2(consumer);
  34.     t1.join();
  35.     // 这里可以添加适当的机制来停止消费者线程
  36.     // 例如使用一个标志位
  37.     return 0;
  38. }
  39. /*
  40. 1. producer 线程每隔 1 秒向队列中添加一个数据,并调用 data_cond.notify_one() 通知消费者线程。
  41. 2. consumer 线程在 data_cond.wait(lock, [] { return!data_queue.empty(); }) 处等待,直到队列中有数据。当有数据时,它会取出数据并处理。
  42. */
复制代码
3. 超时期待



  • 焦点概念
    在某些情况下,线程不能无穷期地期待某个条件满足,须要设置一个超时时间。std::condition_variable 提供了 wait_for 和 wait_until 方法来实现超时期待。
  • 代码
  1. #include <iostream>
  2. #include <thread>
  3. #include <mutex>
  4. #include <condition_variable>
  5. #include <chrono>
  6. std::mutex mtx;
  7. std::condition_variable cv;
  8. bool ready = false;
  9. void worker() {
  10.     std::unique_lock<std::mutex> lock(mtx);
  11.     // 等待 2 秒
  12.     auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(2);
  13.     if (cv.wait_until(lock, timeout, [] { return ready; })) {
  14.         std::cout << "Worker thread is working after being notified." << std::endl;
  15.     } else {
  16.         std::cout << "Worker thread timed out." << std::endl;
  17.     }
  18. }
  19. void prepare_work() {
  20.     std::this_thread::sleep_for(std::chrono::seconds(3));
  21.     {
  22.         std::lock_guard<std::mutex> lock(mtx);
  23.         ready = true;
  24.     }
  25.     cv.notify_one();
  26.     std::cout << "Work is prepared." << std::endl;
  27. }
  28. int main() {
  29.     std::thread t1(worker);
  30.     std::thread t2(prepare_work);
  31.     t1.join();
  32.     t2.join();
  33.     return 0;
  34. }
  35. /*
  36. - worker 线程使用 cv.wait_until(lock, timeout, [] { return ready; }) 等待 ready 条件满足,最多等待 2 秒。
  37. - prepare_work 线程在 3 秒后才将 ready 置为 true 并通知 worker 线程,因此 worker 线程会超时。
  38. */
复制代码
4. std::future 和 std::promise

焦点概念

std::future:表现一个异步操纵的结果,它可以用来获取异步任务的返回值。std::future 对象可以通过 get() 方法来壅闭期待异步操纵完成并获取结果。
std::promise:是一个可以存储值或非常的对象,它可以与 std::future 关联,用于在差异线程之间转达结果。std::promise 对象可以通过 set_value() 方法设置结果,或通过 set_exception() 方法设置非常。
代码

  1. #include <iostream>
  2. #include <thread>
  3. #include <future>
  4. int calculate_sum(int a, int b) {
  5.     return a + b;
  6. }
  7. int main() {
  8.     // 创建一个 std::promise 对象
  9.     std::promise<int> prom;
  10.     // 获取与 promise 关联的 future
  11.     std::future<int> fut = prom.get_future();
  12.     std::thread t([&prom]() {
  13.         int result = calculate_sum(3, 5);
  14.         // 设置 promise 的值
  15.         prom.set_value(result);
  16.     });
  17.     // 获取 future 的值
  18.     int sum = fut.get();
  19.     std::cout << "The sum is: " << sum << std::endl;
  20.     t.join();
  21.     return 0;
  22. }
  23. /*
  24. - 创建 std::promise<int> prom 和与之关联的 std::future<int> fut。
  25. - 启动一个新线程,在该线程中调用 calculate_sum 函数计算结果,并使用 prom.set_value(result) 设置 promise 的值。
  26. - 在主线程中,使用 fut.get() 获取异步操作的结果。
  27. */
复制代码
5. std::packaged_task 和 std::async

焦点概念



  • std::packaged_task: 包装一个可调用对象(如函数、函数对象或 lambda 表达式),并将其结果与 std::future 关联。可以通过调用 std::packaged_task 对象来实验包装的可调用对象,并通过关联的 std::future 获取结果。
  • std::async:是一个更高级的异步操纵启动方式,它可以主动管理线程和返回结果。std::async 担当一个可调用对象和其参数,返回一个 std::future 对象,用于获取异步操纵的结果。
  • 示例代码:
  1. #include <iostream>
  2. #include <thread>
  3. #include <future>
  4. int add(int a, int b) {
  5.     return a + b;
  6. }
  7. int main() {
  8.     // 创建一个 packaged_task 对象
  9.     std::packaged_task<int(int, int)> task(add);
  10.     // 获取与 packaged_task 关联的 future
  11.     std::future<int> fut = task.get_future();
  12.     std::thread t(std::move(task), 3, 5);
  13.     // 获取 future 的值
  14.     int result = fut.get();
  15.     std::cout << "The result is: " << result << std::endl;
  16.     t.join();
  17.     return 0;
  18. }
  19. /*
  20. - 创建 std::packaged_task<int(int, int)> task(add) 包装 add 函数,并获取与之关联的 std::future<int> fut。
  21. - 启动一个新线程,将 task 移动到线程中执行,并传递参数 3 和 5。
  22. - 在主线程中,使用 fut.get() 获取异步操作的结果。
  23. */
复制代码
6. 多选标题


  • 关于 std::condition_variable,以下说法精确的是( )
    A. std::condition_variable 必须与 std::mutex 一起使用
    B. 调用 wait 方法时,假如不使用谓词,大概会发生虚伪唤醒
    C. notify_one 方法会唤醒全部期待的线程
    D. wait_for 和 wait_until 方法可以实现超时期待
  • 在生产者 - 斲丧者模子中,以下哪些做法可以进步性能和安全性( )
    A. 生产者和斲丧者使用同一个互斥量掩护共享队列
    B. 生产者在添加数据后实时关照斲丧者
    C. 斲丧者在处置处罚数据时长时间持有锁
    D. 使用谓词来克制斲丧者的虚伪唤醒
  • 关于 std::future 和 std::promise,以下说法精确的是( )
    A. std::future 可以用来获取异步操纵的结果
    B. std::promise 可以存储值或非常
    C. 一个 std::promise 只能与一个 std::future 关联
    D. std::future 的 get 方法可以多次调用
  • 以下哪些情况大概导致线程的虚伪唤醒( )
    A. 操纵体系的调治战略
    B. 硬件停止
    C. 信号处置处罚
    D. 调用 notify_one 或 notify_all 方法
  • 在使用 std::condition_variable 时,以下做法精确的是( )
    A. 在调用 wait 方法前先锁定互斥量
    B. 在调用 notify_one 或 notify_all 方法时持有互斥量
    C. 使用谓词来克制虚伪唤醒
    D. 在 wait 方法返回后再次查抄条件
6. 多选标题答案


  • 答案:ABD
    表明:std::condition_variable 依靠 std::mutex 来掩护共享数据,以是必须与 std::mutex 一起使用,A 精确;不使用谓词时,wait 方法大概会因操纵体系或硬件的缘故起因发生虚伪唤醒,B 精确;notify_one 方法只会唤醒一个期待的线程,notify_all 才会唤醒全部期待的线程,C 错误;wait_for 和 wait_until 方法可以让线程在期待肯定时间后主动返回,实现超时期待,D 精确。
  • 答案:ABD
    表明:生产者和斲丧者使用同一个互斥量掩护共享队列可以克制数据竞争,包管线程安全,A 精确;生产者在添加数据后实时关照斲丧者可以淘汰斲丧者的期待时间,进步性能,B 精确;斲丧者在处置处罚数据时长时间持有锁会导致其他线程无法访问共享队列,低落并发性能,C 错误;使用谓词来克制斲丧者的虚伪唤醒可以确保斲丧者只在队列中有数据时才举行处置处罚,进步步调的精确性和性能,D 精确。
  • 答案:ABC
    表明:std::future 紧张用于获取异步操纵的结果,A 精确;std::promise 可以存储一个值或非常,并通过关联的 std::future 转达给其他线程,B 精确;一个 std::promise 只能与一个 std::future 关联,C 精确;std::future 的 get 方法只能调用一次,多次调用会导致未界说举动,D 错误。
  • 答案:ABC
    表明:操纵体系的调治战略、硬件停止和信号处置处罚等因素都大概导致线程的虚伪唤醒,A、B、C 精确;调用 notify_one 或 notify_all 方法是正常的关照机制,不会导致虚伪唤醒,D 错误。
  • 答案:ACD
    表明:在调用 wait 方法前先锁定互斥量是须要的,由于 wait 方法会在期待时主动开释锁,在被唤醒后重新获取锁,A 精确;调用 notify_one 或 notify_all 方法时不须要持有互斥量,B 错误;使用谓词可以克制虚伪唤醒,确保线程只在条件满足时才继承实验,C 精确;在 wait 方法返回后再次查抄条件可以进一步确保步调的精确性,D 精确。
7. 操持标题

7.1 操持一个线程安全的缓存体系

要求:


  • 多个线程可查询键是否存在,若不存在则触发盘算任务
  • 束缚:同一键同时只允许一个盘算任务
7.2 实现可取消的并行任务

要求:


  • 任务提交后返回future,允许调用方取消正在实验的任务
  • 须要思量资源整理和线程停止安全
7.3 构建生产者-斲丧者管道

要求:


  • 多个生产者和斲丧者通过有界缓冲区通讯
  • 斲丧者须要优先处置处罚特定范例的消息
7.4 操持超时安全的数据库毗连池

要求:


  • 线程获取毗连时最多期待500ms,超时返回错误
  • 毗连空闲凌驾2分钟主动采取
7.5 实现异步日志日志体系

要求:


  • 全部日志日志写入哀求异步实验
  • 当日志日志队列积存凌驾1000条时切换为同步模式
  • 支持刷盘操纵期待全部待写日志完成
7.1 参考答案

  1. #include <iostream>
  2. #include <mutex>
  3. #include <future>
  4. #include <unordered_map>
  5. template<typename Key, typename Value>
  6. class SafeCache {
  7.     std::mutex mtx;
  8.     std::unordered_map<Key, Value> cache;
  9.     std::unordered_map<Key, std::shared_future<Value>> pending;
  10.     Value compute(const Key& key) {
  11.         // 模拟耗时计算
  12.         std::this_thread::sleep_for(std::chrono::seconds(1));
  13.         return key + "_value";
  14.     }
  15. public:
  16.     Value get(const Key& key) {
  17.         std::unique_lock lock(mtx);
  18.         if (auto it = cache.find(key); it != cache.end()) {
  19.             return it->second;
  20.         }
  21.         if (auto it = pending.find(key); it != pending.end()) {
  22.             auto fut = it->second;
  23.             lock.unlock();
  24.             return fut.get();
  25.         }
  26.         auto promise = std::make_shared<std::promise<Value>>();
  27.         auto fut = promise->get_future().share();
  28.         pending.emplace(key, fut);
  29.         lock.unlock();
  30.         try  {
  31.             auto res = compute(key);
  32.             lock.lock();
  33.             cache.emplace(key, res);
  34.             pending.erase(key);
  35.             lock.unlock();
  36.             promise->set_value(res);
  37.         } catch (...) {
  38.             promise->set_exception(std::current_exception());
  39.         }
  40.         return fut.get();
  41.     }
  42. };
  43. // 测试用例
  44. void test_cache() {
  45.     SafeCache<std::string, std::string> cache;
  46.     auto task = [&](std::string key) {
  47.         return cache.get(key);
  48.     };
  49.     auto fut1 = std::async(std::launch::async, task, "A");
  50.     auto fut2 = std::async(std::launch::async, task, "A");
  51.    
  52.     std::cout << fut1.get() << std::endl; // A_value
  53.     std::cout << fut2.get() << std::endl; // A_value (仅一次计算)
  54. }
  55. int main() {
  56.     test_cache();
  57.     return 0;
  58. }
  59. /*
  60. 分析:
  61. 使用std:mutex保护缓存的访问,防止数据竟争。
  62. 当键不存在时,为避免多个线程同时触发计算,使用互斥锁和条件变量来保证单个任务执行。可能需要存储计算中的键,防止重复计算。可以使用std::unordered_map存储键和对应的future,表示计算中的结果:
  63. 步骤:
  64. 1.定义缓存数据结构,包括互斥锁、存储键值对以及正在计算的键及其future。
  65. 2.查询缓存时锁住互斥锁,若存在则直接返回值。
  66. 3.如果键不存在且未被计算,则生成一个promise和future,存储到正在计算的map中,并启动异步任务计算结果
  67. 4.其他线程查询同一键时,等待该键对应的future就绪。
  68. 测试用例:
  69. 多线程同时请求相同的键,确保只有一个计算任务执行
  70. 验证缓存结果是否正确,避免重复计算。
  71. 处理计算中的异常情况。
  72. */
复制代码
7.2 参考答案

  1. #include <iostream>
  2. #include <future>
  3. #include <atomic>
  4. #include <chrono>
  5. #include <functional>
  6. // 自定义可取消的任务包装类
  7. template <typename Result>
  8. class CancellableTask {
  9. public:
  10.     // 构造函数,接受一个可调用对象和一个取消标志
  11.     template <typename Func>
  12.     CancellableTask(Func func, std::shared_ptr<std::atomic<bool>> cancelFlag)
  13.         : cancelFlag_(cancelFlag) {
  14.         // 使用 std::async 异步执行任务
  15.         future_ = std::async(std::launch::async, [func, cancelFlag]() {
  16.             while (!cancelFlag->load()) {
  17.                 // 执行任务函数
  18.                 Result result = func();
  19.                 return result;
  20.             }
  21.             // 如果任务被取消,抛出异常
  22.             throw std::runtime_error("Task cancelled");
  23.         });
  24.     }
  25.     // 获取任务的 future 对象
  26.     std::future<Result> get_future() {
  27.         return std::move(future_);
  28.     }
  29.     // 取消任务
  30.     void cancel() {
  31.         cancelFlag_->store(true);
  32.     }
  33. private:
  34.     std::future<Result> future_;
  35.     std::shared_ptr<std::atomic<bool>> cancelFlag_;
  36. };
  37. // 示例任务函数
  38. int exampleTask() {
  39.     for (int i = 0; i < 5; ++i) {
  40.         std::this_thread::sleep_for(std::chrono::seconds(1));
  41.         std::cout << "Task is working: " << i << std::endl;
  42.     }
  43.     return 42;
  44. }
  45. int main() {
  46.     // 创建一个共享的原子布尔变量作为取消标志
  47.     auto cancelFlag = std::make_shared<std::atomic<bool>>(false);
  48.     // 创建可取消的任务
  49.     CancellableTask<int> task(exampleTask, cancelFlag);
  50.     // 获取任务的 future 对象
  51.     auto future = task.get_future();
  52.     // 模拟一段时间后取消任务
  53.     std::this_thread::sleep_for(std::chrono::seconds(2));
  54.     std::cout << "Cancelling the task..." << std::endl;
  55.     task.cancel();
  56.     try {
  57.         // 尝试获取任务的结果
  58.         int result = future.get();
  59.         std::cout << "Task result: " << result << std::endl;
  60.     } catch (const std::runtime_error& e) {
  61.         std::cout << "Exception caught: " << e.what() << std::endl;
  62.     }
  63.     return 0;
  64. }
复制代码
7.3 参考答案

  1. #include <iostream>
  2. #include <vector>
  3. #include <queue>
  4. #include <thread>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <algorithm>
  8. #include <chrono>
  9. // 定义消息类型
  10. const int PRIORITY_MESSAGE_TYPE = 1;
  11. // 有界缓冲区类
  12. template<typename T>
  13. class BoundedBuffer {
  14. private:
  15.     std::vector<T> buffer;
  16.     std::mutex mtx;
  17.     std::condition_variable not_full;
  18.     std::condition_variable not_empty;
  19.     size_t capacity;
  20. public:
  21.     BoundedBuffer(size_t cap) : capacity(cap) {}
  22.     // 生产者向缓冲区添加消息
  23.     void enqueue(const T& item) {
  24.         std::unique_lock<std::mutex> lock(mtx);
  25.         // 等待缓冲区有空间
  26.         not_full.wait(lock, [this] { return buffer.size() < capacity; });
  27.         buffer.push_back(item);
  28.         // 通知消费者缓冲区有新消息
  29.         not_empty.notify_one();
  30.     }
  31.     // 消费者从缓冲区取出消息,优先处理特定类型的消息
  32.     T dequeue() {
  33.         std::unique_lock<std::mutex> lock(mtx);
  34.         // 等待缓冲区有消息
  35.         not_empty.wait(lock, [this] { return!buffer.empty(); });
  36.         // 优先查找特定类型的消息
  37.         auto it = std::find_if(buffer.begin(), buffer.end(), [](const T& msg) {
  38.             return std::get<1>(msg) == PRIORITY_MESSAGE_TYPE;
  39.         });
  40.         T item;
  41.         if (it != buffer.end()) {
  42.             item = *it;
  43.             buffer.erase(it);
  44.         } else {
  45.             item = buffer.front();
  46.             buffer.erase(buffer.begin());
  47.         }
  48.         // 通知生产者缓冲区有空间
  49.         not_full.notify_one();
  50.         return item;
  51.     }
  52. };
  53. // 生产者函数
  54. void producer(BoundedBuffer<std::pair<int, int>>& buffer, int id) {
  55.     for (int i = 0; i < 5; ++i) {
  56.         int messageType = (i % 2 == 0)? PRIORITY_MESSAGE_TYPE : 2;
  57.         buffer.enqueue({id * 10 + i, messageType});
  58.         std::cout << "Producer " << id << " produced message " << id * 10 + i << " of type " << messageType << std::endl;
  59.         std::this_thread::sleep_for(std::chrono::milliseconds(100));
  60.     }
  61. }
  62. // 消费者函数
  63. void consumer(BoundedBuffer<std::pair<int, int>>& buffer, int id) {
  64.     for (int i = 0; i < 5; ++i) {
  65.         auto [messageId, messageType] = buffer.dequeue();
  66.         std::cout << "Consumer " << id << " consumed message " << messageId << " of type " << messageType << std::endl;
  67.         std::this_thread::sleep_for(std::chrono::milliseconds(200));
  68.     }
  69. }
  70. int main() {
  71.     const int bufferCapacity = 10;
  72.     BoundedBuffer<std::pair<int, int>> buffer(bufferCapacity);
  73.     const int numProducers = 3;
  74.     const int numConsumers = 2;
  75.     std::vector<std::thread> producers;
  76.     std::vector<std::thread> consumers;
  77.     // 创建生产者线程
  78.     for (int i = 0; i < numProducers; ++i) {
  79.         producers.emplace_back(producer, std::ref(buffer), i);
  80.     }
  81.     // 创建消费者线程
  82.     for (int i = 0; i < numConsumers; ++i) {
  83.         consumers.emplace_back(consumer, std::ref(buffer), i);
  84.     }
  85.     // 等待生产者线程结束
  86.     for (auto& p : producers) {
  87.         p.join();
  88.     }
  89.     // 等待消费者线程结束
  90.     for (auto& c : consumers) {
  91.         c.join();
  92.     }
  93.     return 0;
  94. }
复制代码
7.4 参考答案

  1. #include <iostream>
  2. #include <queue>
  3. #include <thread>
  4. #include <mutex>
  5. #include <condition_variable>
  6. #include <chrono>
  7. #include <vector>
  8. // 模拟数据库连接类
  9. class DatabaseConnection {
  10. public:
  11.     DatabaseConnection() {
  12.         std::cout << "Database connection created." << std::endl;
  13.     }
  14.     ~DatabaseConnection() {
  15.         std::cout << "Database connection destroyed." << std::endl;
  16.     }
  17.     void executeQuery(const std::string& query) {
  18.         std::cout << "Executing query: " << query << std::endl;
  19.     }
  20. };
  21. // 数据库连接池类
  22. class DatabaseConnectionPool {
  23. private:
  24.     std::queue<DatabaseConnection*> connections;
  25.     std::mutex mtx;
  26.     std::condition_variable cv;
  27.     size_t maxConnections;
  28.     std::chrono::time_point<std::chrono::steady_clock> lastUsed;
  29.     // 自动回收线程函数
  30.     void autoReclaim() {
  31.         while (true) {
  32.             std::this_thread::sleep_for(std::chrono::minutes(1)); // 每分钟检查一次
  33.             {
  34.                 std::unique_lock<std::mutex> lock(mtx);
  35.                 auto now = std::chrono::steady_clock::now();
  36.                 while (!connections.empty()) {
  37.                     auto connection = connections.front();
  38.                     auto idleTime = std::chrono::duration_cast<std::chrono::minutes>(now - lastUsed);
  39.                     if (idleTime.count() >= 2) {
  40.                         connections.pop();
  41.                         delete connection;
  42.                     } else {
  43.                         break;
  44.                     }
  45.                 }
  46.             }
  47.         }
  48.     }
  49. public:
  50.     DatabaseConnectionPool(size_t maxConns) : maxConnections(maxConns) {
  51.         for (size_t i = 0; i < maxConns; ++i) {
  52.             connections.push(new DatabaseConnection());
  53.         }
  54.         lastUsed = std::chrono::steady_clock::now();
  55.         std::thread(&DatabaseConnectionPool::autoReclaim, this).detach();
  56.     }
  57.     ~DatabaseConnectionPool() {
  58.         std::unique_lock<std::mutex> lock(mtx);
  59.         while (!connections.empty()) {
  60.             delete connections.front();
  61.             connections.pop();
  62.         }
  63.     }
  64.     // 获取数据库连接
  65.     DatabaseConnection* getConnection() {
  66.         std::unique_lock<std::mutex> lock(mtx);
  67.         if (cv.wait_for(lock, std::chrono::milliseconds(500), [this] { return!connections.empty(); })) {
  68.             auto connection = connections.front();
  69.             connections.pop();
  70.             lastUsed = std::chrono::steady_clock::now();
  71.             return connection;
  72.         }
  73.         return nullptr; // 超时返回 nullptr
  74.     }
  75.     // 释放数据库连接
  76.     void releaseConnection(DatabaseConnection* connection) {
  77.         std::unique_lock<std::mutex> lock(mtx);
  78.         connections.push(connection);
  79.         lastUsed = std::chrono::steady_clock::now();
  80.         cv.notify_one();
  81.     }
  82. };
  83. // 测试用例
  84. void testConnectionPool() {
  85.     DatabaseConnectionPool pool(3);
  86.     auto task = [&pool]() {
  87.         DatabaseConnection* conn = pool.getConnection();
  88.         if (conn) {
  89.             conn->executeQuery("SELECT * FROM users");
  90.             pool.releaseConnection(conn);
  91.         } else {
  92.             std::cout << "Failed to get a connection (timeout)." << std::endl;
  93.         }
  94.     };
  95.     std::vector<std::thread> threads;
  96.     for (int i = 0; i < 5; ++i) {
  97.         threads.emplace_back(task);
  98.     }
  99.     for (auto& t : threads) {
  100.         t.join();
  101.     }
  102. }
  103. int main() {
  104.     testConnectionPool();
  105.     return 0;
  106. }
复制代码
7.5 参考答案

  1. #include <iostream>
  2. #include <fstream>
  3. #include <queue>
  4. #include <thread>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <string>
  8. #include <chrono>
  9. #include <atomic>
  10. #include <vector>
  11. // 定义日志级别
  12. enum class LogLevel {
  13.     DEBUG,
  14.     INFO,
  15.     WARN,
  16.     ERROR
  17. };
  18. class AsyncLogger {
  19. private:
  20.     std::queue<std::string> logQueue;
  21.     std::mutex mtx;
  22.     std::condition_variable cv;
  23.     std::ofstream logFile;
  24.     std::thread loggerThread;
  25.     std::atomic<bool> stopLogging;
  26.     std::atomic<bool> isSyncMode;
  27.     static constexpr size_t MAX_QUEUE_SIZE = 1000;
  28.     // 日志线程函数
  29.     void logger() {
  30.         std::vector<std::string> buffer;
  31.         while (!stopLogging) {
  32.             {
  33.                 std::unique_lock<std::mutex> lock(mtx);
  34.                 cv.wait_for(lock, std::chrono::milliseconds(100), [this] { return!logQueue.empty() || stopLogging; });
  35.                 while (!logQueue.empty()) {
  36.                     buffer.push_back(std::move(logQueue.front()));
  37.                     logQueue.pop();
  38.                 }
  39.             }
  40.             for (const auto& msg : buffer) {
  41.                 try {
  42.                     logFile << msg << std::endl;
  43.                 } catch (const std::exception& e) {
  44.                     std::cerr << "Error writing log: " << e.what() << std::endl;
  45.                 }
  46.             }
  47.             buffer.clear();
  48.         }
  49.         // 处理剩余的日志
  50.         {
  51.             std::unique_lock<std::mutex> lock(mtx);
  52.             while (!logQueue.empty()) {
  53.                 try {
  54.                     logFile << logQueue.front() << std::endl;
  55.                     logQueue.pop();
  56.                 } catch (const std::exception& e) {
  57.                     std::cerr << "Error writing remaining log: " << e.what() << std::endl;
  58.                 }
  59.             }
  60.         }
  61.         try {
  62.             logFile.flush();
  63.         } catch (const std::exception& e) {
  64.             std::cerr << "Error flushing log file: " << e.what() << std::endl;
  65.         }
  66.     }
  67.     // 格式化日志消息,添加日志级别和时间戳
  68.     std::string formatLogMessage(LogLevel level, const std::string& message) {
  69.         auto now = std::chrono::system_clock::now();
  70.         std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
  71.         std::string timeStr = std::ctime(&currentTime);
  72.         timeStr.pop_back(); // 去掉换行符
  73.         std::string levelStr;
  74.         switch (level) {
  75.             case LogLevel::DEBUG:
  76.                 levelStr = "DEBUG";
  77.                 break;
  78.             case LogLevel::INFO:
  79.                 levelStr = "INFO";
  80.                 break;
  81.             case LogLevel::WARN:
  82.                 levelStr = "WARN";
  83.                 break;
  84.             case LogLevel::ERROR:
  85.                 levelStr = "ERROR";
  86.                 break;
  87.         }
  88.         return "[" + timeStr + "] [" + levelStr + "] " + message;
  89.     }
  90. public:
  91.     AsyncLogger(const std::string& logFileName) : stopLogging(false), isSyncMode(false) {
  92.         try {
  93.             logFile.open(logFileName, std::ios::app);
  94.             if (!logFile.is_open()) {
  95.                 std::cerr << "Failed to open log file: " << logFileName << std::endl;
  96.             }
  97.         } catch (const std::exception& e) {
  98.             std::cerr << "Error opening log file: " << e.what() << std::endl;
  99.         }
  100.         loggerThread = std::thread(&AsyncLogger::logger, this);
  101.     }
  102.     ~AsyncLogger() {
  103.         stopLogging = true;
  104.         cv.notify_one();
  105.         if (loggerThread.joinable()) {
  106.             loggerThread.join();
  107.         }
  108.         try {
  109.             logFile.close();
  110.         } catch (const std::exception& e) {
  111.             std::cerr << "Error closing log file: " << e.what() << std::endl;
  112.         }
  113.     }
  114.     // 写入日志
  115.     void log(LogLevel level, const std::string& message) {
  116.         std::string formattedMessage = formatLogMessage(level, message);
  117.         {
  118.             std::unique_lock<std::mutex> lock(mtx);
  119.             if (logQueue.size() >= MAX_QUEUE_SIZE) {
  120.                 isSyncMode = true;
  121.             } else {
  122.                 isSyncMode = false;
  123.             }
  124.         }
  125.         if (isSyncMode) {
  126.             // 同步模式,直接写入日志
  127.             std::lock_guard<std::mutex> lock(mtx);
  128.             try {
  129.                 logFile << formattedMessage << std::endl;
  130.                 logFile.flush();
  131.             } catch (const std::exception& e) {
  132.                 std::cerr << "Error writing log in sync mode: " << e.what() << std::endl;
  133.             }
  134.         } else {
  135.             // 异步模式,将日志消息放入队列
  136.             {
  137.                 std::lock_guard<std::mutex> lock(mtx);
  138.                 logQueue.push(formattedMessage);
  139.             }
  140.             cv.notify_one();
  141.         }
  142.     }
  143.     // 刷盘操作
  144.     void flush() {
  145.         std::unique_lock<std::mutex> lock(mtx);
  146.         while (!logQueue.empty()) {
  147.             cv.wait(lock, [this] { return logQueue.empty(); });
  148.         }
  149.         try {
  150.             logFile.flush();
  151.         } catch (const std::exception& e) {
  152.             std::cerr << "Error flushing log during flush operation: " << e.what() << std::endl;
  153.         }
  154.     }
  155. };
  156. // 测试用例
  157. void testAsyncLogger() {
  158.     AsyncLogger logger("test.log");
  159.     // 模拟大量日志写入
  160.     for (int i = 0; i < 2000; ++i) {
  161.         logger.log(LogLevel::INFO, "Log message " + std::to_string(i));
  162.         std::cout << "Log message " << i << std::endl;  // 输出到控制台
  163.     }
  164.     // 刷盘操作
  165.     logger.flush();
  166. }
  167. int main() {
  168.     testAsyncLogger();
  169.     return 0;
  170. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表