读者写者与生产者消费者应用场景

在并发编程中,读写场景和生产者消费者场景是非常经典的两种问题。它们的基本思路和实现方法在许多应用中都可以找到。下面分别介绍这两种场景的一些经典问题及其解决方案。

读写场景

经典问题

  1. 多线程访问共享资源

    • 多个线程需要同时读取某个共享数据(例如,配置文件、缓存等),但在某个线程需要写入时,其他线程必须等待。
  2. 统计信息更新

    • 某个程序模块需要定期更新统计信息,其他模块在此期间仍然可以读取这些信息,而在更新时不允许其他线程读取。

解决方案

  • 读写锁:使用 shared_mutex 来实现多个线程的并发读取,而在写入时使用 unique_lock 确保独占访问。
// 伪代码示例
class SharedData {
private:
    int data;
    mutable std::shared_mutex mutex;

public:
    // 读取数据
    int read() const {
        std::shared_lock<std::shared_mutex> lock(mutex);
        return data;
    }

    // 写入数据
    void write(int value) {
        std::unique_lock<std::shared_mutex> lock(mutex);
        data = value;
    }
};

生产者消费者场景

经典问题

  1. 消息队列

    • 消息生产者将消息放入队列,消费者从队列中取出消息进行处理。需要保证在队列为空时消费者等待,而在队列满时生产者等待。
  2. 任务调度

    • 任务生成器将任务添加到任务队列中,而工作线程从任务队列中取出任务并执行。需要处理并发安全和阻塞条件。

解决方案

  • 条件变量:结合 mutexcondition_variable 来实现生产者和消费者之间的协调。
#include <iostream>
#include <queue>
#include <thread>
#include <condition_variable>

template <typename T>
class BoundedBuffer {
private:
    std::queue<T> buffer;
    size_t max_size;
    std::mutex mtx;
    std::condition_variable not_empty;
    std::condition_variable not_full;

public:
    BoundedBuffer(size_t size) : max_size(size) {}

    void produce(T item) {
        std::unique_lock<std::mutex> lock(mtx);
        not_full.wait(lock, [this] { return buffer.size() < max_size; });
        buffer.push(item);
        not_empty.notify_one();
    }

    T consume() {
        std::unique_lock<std::mutex> lock(mtx);
        not_empty.wait(lock, [this] { return !buffer.empty(); });
        T item = buffer.front();
        buffer.pop();
        not_full.notify_one();
        return item;
    }
};

总结

  • 读写场景强调了多个线程如何有效地访问共享资源,尤其是在读取操作多于写入操作时。使用读写锁是一种常见的解决方案。
  • 生产者消费者场景则侧重于在多线程环境中协调数据的生产和消费,确保数据的安全性和系统的高效性。利用条件变量和互斥量是解决此问题的经典方式。

这两种场景广泛应用于数据库、缓存、实时处理系统等各种领域,是并发编程中的重要概念。

posted @ 2024-10-06 20:01  牛马chen  阅读(15)  评论(0编辑  收藏  举报