【C/C++】Observer与Producer-Consumer模式解析

Observer Pattern

The Observer Pattern is a behavioral design pattern used to allow an object (called the subject ) to notify a list of observers when its state changes. The pattern defines a one-to-many relationship between objects. When the subject changes, all registered observers are automatically notified.

Key Components:
  • Subject: The object that maintains a list of observers. It notifies the observers when its state changes.
  • Observer : An interface or abstract class that defines the update method. Concrete observers implement this method to react to changes in the subject.
  • ConcreteSubject: A subclass of the subject, which has the state being tracked and notifies observers when the state changes.
  • ConcreteObserver: A concrete class that implements the observer interface to react to changes in the subject.
Use Case:

The Observer Pattern is often used when multiple objects need to react to changes in the state of another object. For example, UI frameworks use the observer pattern to update the display when the underlying data changes.

Synchronization in Observer Pattern:

The Observer pattern can be used in both synchronous and asynchronous environments. The key aspect of the pattern is the notification of observers, which can be synchronous or asynchronous depending on how the update method is invoked.

  • Synchronous : In a basic implementation, when the subject's state changes, it calls the update method of all its observers synchronously. The observers will execute in the same thread as the subject.

  • Asynchronous: In more advanced implementations, observers may be notified asynchronously, often using queues, background threads, or events to trigger the observers' updates.

Producer-Consumer Pattern

The Producer-Consumer Pattern is a concurrency pattern used to decouple tasks by using a buffer between producers and consumers. The producer is responsible for producing data (e.g., producing items, events, or tasks), while the consumer processes or consumes that data. The buffer, often implemented as a queue, holds the data temporarily until the consumer can process it.

Key Components:
  • Producer: An entity that creates data and adds it to the buffer.
  • Consumer: An entity that removes data from the buffer and processes it.
  • Buffer (Queue): A shared resource (typically a queue) that holds the data between the producer and the consumer. It can be implemented in various ways, such as using a thread-safe queue.
Use Case:

The Producer-Consumer pattern is commonly used in systems where one or more producers generate tasks (or data) that need to be consumed by one or more consumers. Examples include:

  • Task scheduling in a multi-threaded environment.
  • Web server request processing.
  • Data streaming or logging systems.
Asynchronous Nature of Producer-Consumer:

In the Producer-Consumer pattern, operations are typically asynchronous. Here's why:

  • Producers can create data at different rates from the consumers. The producer might be slower or faster than the consumer, and the buffer can store the produced data temporarily.
  • Consumers process data from the buffer asynchronously, which can involve waiting for data or consuming data as it arrives. They don't block the producer and can process data in parallel with it.

Key Differences and When to Use Them

  • Observer Pattern is primarily about notifying and updating. It's most commonly used to update different components when a change occurs in the subject. Observers can be registered to listen for changes in a subject and respond to those changes, often used for UI updates, event handling, and notifications.

  • Producer-Consumer Pattern is focused on asynchronous task management. It decouples data production and consumption, typically used to handle tasks concurrently or to ensure that the producer and consumer don't overwhelm each other, especially when data arrives at unpredictable rates.

Are They Used in Sync or Async?
  • Observer Pattern can be both synchronous and asynchronous. In synchronous cases, all observers are notified in the same thread as the subject's change. In asynchronous cases, observers can be notified on separate threads or events can be queued for later execution.

  • Producer-Consumer Pattern is typically asynchronous because it's designed for handling tasks concurrently. The producer and consumer don't operate at the same rate, and the queue acts as a buffer to prevent the producer from overwhelming the consumer or vice versa.

Example Code in C++

Observer Pattern Example (Synchronous):
cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// Observer Interface
class Observer {
public:
    virtual void update(const std::string& message) = 0;
};

// Concrete Observer
class ConcreteObserver : public Observer {
public:
    void update(const std::string& message) override {
        std::cout << "Observer received: " << message << std::endl;
    }
};

// Subject Class
class Subject {
private:
    std::vector<Observer*> observers;

public:
    void addObserver(Observer* observer) {
        observers.push_back(observer);
    }

    void removeObserver(Observer* observer) {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers(const std::string& message) {
        for (auto& observer : observers) {
            observer->update(message);
        }
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1, observer2;
    subject.addObserver(&observer1);
    subject.addObserver(&observer2);
    
    subject.notifyObservers("Hello Observers!");
    return 0;
}
Producer-Consumer Pattern Example (Asynchronous):
cpp 复制代码
#include <iostream>
#include <thread>
#include <queue>
#include <condition_variable>
#include <atomic>

// Shared Queue and Sync Mechanism
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> done(false);

void producer() {
    for (int i = 0; i < 10; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::lock_guard<std::mutex> lock(mtx);
        buffer.push(i);
        std::cout << "Produced: " << i << std::endl;
        cv.notify_all();  // Notify consumers
    }
    done = true;
    cv.notify_all();  // Notify consumer to exit
}

void consumer() {
    while (!done || !buffer.empty()) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !buffer.empty() || done; });  // Wait for data to consume

        if (!buffer.empty()) {
            int data = buffer.front();
            buffer.pop();
            std::cout << "Consumed: " << data << std::endl;
        }
    }
}

int main() {
    std::thread prod(producer);
    std::thread cons(consumer);

    prod.join();
    cons.join();

    return 0;
}

Conclusion:

  • Observer Pattern is about keeping components in sync by notifying them when a change occurs, and it can be both synchronous and asynchronous.
  • Producer-Consumer Pattern is designed for decoupling tasks and is inherently asynchronous, where producers and consumers work in parallel, potentially at different rates, with a shared buffer.

Both patterns serve different purposes: Observer handles event notification and synchronization between components, while Producer-Consumer helps manage concurrency and task flow, typically for performance and load balancing.

相关推荐
csdn_aspnet4 分钟前
C++ 扇形的面积(Area of a Circular Sector)
开发语言·c++
上位机_0x16 分钟前
c#中添加visionpro控件(联合编程)
开发语言·数码相机·c#
plmm烟酒僧34 分钟前
在 Ubuntu 下通过 C APP程序实现串口发送数据并接收返回数据
linux·c语言·ubuntu·计算机外设·串口·ch340
cykaw259042 分钟前
QT中信号和事件的区别
开发语言·qt
摆烂仙君44 分钟前
手搓四人麻将程序
开发语言·python
weixin_428498491 小时前
NVC++ 介绍与使用指南
c++·cuda
Susea&1 小时前
趣味编程:抽象图(椭圆组成)
c语言·c++·技术美术·easyx绘图
木易小熙1 小时前
chromedp -—— 基于 go 的自动化操作浏览器库
开发语言·golang·自动化
天天进步20151 小时前
C# Prism框架详解:构建模块化WPF应用程序
开发语言·c#·wpf
_extraordinary_2 小时前
Java 继承
java·开发语言·继承