文章目錄
- C語言發布訂閱模式詳解與實踐
- 1. 什么是發布訂閱模式?
- 2. 為什么需要發布訂閱模式?
- 3. 實際應用場景
- 4. 代碼實現
- 4.1 UML 關系圖
- 4.2 頭文件 (pubsub.h)
- 4.3 實現文件 (pubsub.c)
- 4.4 使用示例 (main.c)
- 5. 代碼分析
- 5.1 關鍵設計點
- 5.2 實現特點
- 6. 編譯和運行
- 7. 注意事項
- 8. 改進建議
- 9. 總結
- 參考資料
C語言發布訂閱模式詳解與實踐
1. 什么是發布訂閱模式?
發布訂閱模式定義了一種一對多的依賴關系,讓多個訂閱者對象同時監聽某一個主題。這個主題在狀態發生變化時,會通知所有依賴于它的訂閱者對象,使它們能夠自動更新。
2. 為什么需要發布訂閱模式?
- 實現對象間的松耦合
- 支持廣播通信
- 動態訂閱和取消訂閱
- 事件驅動架構
- 異步消息處理
3. 實際應用場景
- 傳感器數據分發
- 消息隊列系統
- 事件處理系統
- 日志監控
- 狀態更新通知
4. 代碼實現
4.1 UML 關系圖
4.2 頭文件 (pubsub.h)
#ifndef PUBSUB_H
#define PUBSUB_H#include <stdint.h>
#include <stdbool.h>// 主題數據結構
typedef struct {char name[32]; // 主題名稱void* data; // 主題數據uint32_t data_size; // 數據大小uint32_t timestamp; // 時間戳
} Topic;// 訂閱者回調函數類型
typedef void (*SubscriberCallback)(const Topic* topic, void* user_data);// 訂閱者結構
typedef struct {char name[32]; // 訂閱者名稱SubscriberCallback callback; // 回調函數void* user_data; // 用戶數據
} Subscriber;// 發布者結構
typedef struct {char name[32]; // 發布者名稱Subscriber* subscribers[16]; // 訂閱者列表int subscriber_count; // 訂閱者數量
} Publisher;// 創建發布者
Publisher* create_publisher(const char* name);// 銷毀發布者
void destroy_publisher(Publisher* publisher);// 訂閱主題
bool subscribe(Publisher* publisher, const char* subscriber_name,SubscriberCallback callback,void* user_data);// 取消訂閱
bool unsubscribe(Publisher* publisher, const char* subscriber_name);// 發布主題
void publish(Publisher* publisher, const Topic* topic);#endif // PUBSUB_H
4.3 實現文件 (pubsub.c)
#include "pubsub.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>Publisher* create_publisher(const char* name) {Publisher* publisher = (Publisher*)malloc(sizeof(Publisher));strncpy(publisher->name, name, sizeof(publisher->name) - 1);publisher->subscriber_count = 0;memset(publisher->subscribers, 0, sizeof(publisher->subscribers));return publisher;
}void destroy_publisher(Publisher* publisher) {if (!publisher) return;// 釋放所有訂閱者for (int i = 0; i < publisher->subscriber_count; i++) {free(publisher->subscribers[i]);}free(publisher);
}bool subscribe(Publisher* publisher, const char* subscriber_name,SubscriberCallback callback,void* user_data) {if (!publisher || !subscriber_name || !callback) return false;// 檢查是否已達到最大訂閱者數量if (publisher->subscriber_count >= 16) {printf("訂閱者數量已達上限\n");return false;}// 檢查是否已訂閱for (int i = 0; i < publisher->subscriber_count; i++) {if (strcmp(publisher->subscribers[i]->name, subscriber_name) == 0) {printf("訂閱者 %s 已存在\n", subscriber_name);return false;}}// 創建新訂閱者Subscriber* subscriber = (Subscriber*)malloc(sizeof(Subscriber));strncpy(subscriber->name, subscriber_name, sizeof(subscriber->name) - 1);subscriber->callback = callback;subscriber->user_data = user_data;// 添加到訂閱者列表publisher->subscribers[publisher->subscriber_count++] = subscriber;printf("訂閱者 %s 已添加\n", subscriber_name);return true;
}bool unsubscribe(Publisher* publisher, const char* subscriber_name) {if (!publisher || !subscriber_name) return false;for (int i = 0; i < publisher->subscriber_count; i++) {if (strcmp(publisher->subscribers[i]->name, subscriber_name) == 0) {// 釋放訂閱者free(publisher->subscribers[i]);// 移動后續訂閱者for (int j = i; j < publisher->subscriber_count - 1; j++) {publisher->subscribers[j] = publisher->subscribers[j + 1];}publisher->subscriber_count--;printf("訂閱者 %s 已移除\n", subscriber_name);return true;}}printf("未找到訂閱者 %s\n", subscriber_name);return false;
}void publish(Publisher* publisher, const Topic* topic) {if (!publisher || !topic) return;printf("\n發布者 %s 發布主題 %s\n", publisher->name, topic->name);// 通知所有訂閱者for (int i = 0; i < publisher->subscriber_count; i++) {Subscriber* subscriber = publisher->subscribers[i];printf("通知訂閱者 %s\n", subscriber->name);subscriber->callback(topic, subscriber->user_data);}
}
4.4 使用示例 (main.c)
#include "pubsub.h"
#include <stdio.h>// 溫度傳感器訂閱者回調
void temperature_callback(const Topic* topic, void* user_data) {float* threshold = (float*)user_data;float temperature = *(float*)topic->data;printf("溫度傳感器收到數據: %.1f°C\n", temperature);if (temperature > *threshold) {printf("警告:溫度超過閾值 %.1f°C!\n", *threshold);}
}// 日志記錄訂閱者回調
void logger_callback(const Topic* topic, void* user_data) {printf("日志記錄器:主題 %s, 數據大小 %d, 時間戳 %d\n",topic->name, topic->data_size, topic->timestamp);
}int main() {// 創建發布者Publisher* sensor_publisher = create_publisher("傳感器發布者");// 創建溫度閾值float temp_threshold = 30.0f;// 訂閱主題subscribe(sensor_publisher, "溫度監控器", temperature_callback, &temp_threshold);subscribe(sensor_publisher, "系統日志", logger_callback, NULL);// 創建并發布溫度數據float temp_data[] = {25.5f, 28.3f, 32.7f};for (int i = 0; i < 3; i++) {Topic topic = {.name = "temperature",.data = &temp_data[i],.data_size = sizeof(float),.timestamp = (uint32_t)time(NULL)};publish(sensor_publisher, &topic);}// 取消訂閱unsubscribe(sensor_publisher, "系統日志");// 再次發布數據float final_temp = 35.2f;Topic topic = {.name = "temperature",.data = &final_temp,.data_size = sizeof(float),.timestamp = (uint32_t)time(NULL)};publish(sensor_publisher, &topic);// 清理資源destroy_publisher(sensor_publisher);return 0;
}
5. 代碼分析
5.1 關鍵設計點
- 發布者管理訂閱者列表
- 回調機制實現通知
- 主題數據封裝
- 動態訂閱管理
5.2 實現特點
- 函數指針實現回調
- 支持用戶數據傳遞
- 訂閱者管理完善
- 資源管理安全
6. 編譯和運行
gcc -c pubsub.c -o pubsub.o
gcc -c main.c -o main.o
gcc pubsub.o main.o -o pubsub_demo
7. 注意事項
- 訂閱者數量限制
- 內存管理安全
- 回調函數異常處理
- 線程安全考慮
8. 改進建議
- 添加主題過濾
- 實現異步通知
- 支持優先級訂閱
- 添加訂閱者分組
9. 總結
發布訂閱模式通過解耦發布者和訂閱者,實現了靈活的消息通知機制。這種模式特別適合處理事件驅動的場景。
參考資料
- 《設計模式:可復用面向對象軟件的基礎》
- 《C語言程序設計》
- 《事件驅動編程》