用观察者模式通知UI刷新数据

用观察者模式用UI显示数据变化

  • 主程序
main.cpp 复制代码
#include "QtWidgetsApplication1.h"
#include <QtWidgets/QApplication>
#include "Observer.h"
#include <QDebug>

class UIObserver : public Observer {
public:
	void Execute() override { qDebug() << "[UI] refresh"; }
};

class IoObserver : public Observer {
public:
	void Execute() override
	{
		qDebug() << "[IO] flush";
	}
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    auto& m = Model::GetInstance();

	std::shared_ptr<QtWidgetsApplication1> window = std::make_shared<QtWidgetsApplication1>();
	std::shared_ptr<UIObserver> UI = std::make_shared<UIObserver>();
	std::shared_ptr<IoObserver> OI = std::make_shared<IoObserver>();
	int64_t h1 = m.Register(UI);
	int64_t h2 = m.Register(OI);
	int64_t h3 = m.Register(window);
    window->show();
    return app.exec();
}
  • 逻辑UI
QtWidgetsApplication.h 复制代码
#pragma once

#include <QtWidgets/QWidget>
#include "ui_QtWidgetsApplication1.h"
#include "Observer.h"

class QtWidgetsApplication1 : public QWidget, public Observer
{
    Q_OBJECT

public:
    QtWidgetsApplication1(QWidget *parent = nullptr);
    ~QtWidgetsApplication1();

    void Execute() override;

public slots:
	void onUpdate();
	void doUpdate(int v); // 在主线程执行

private:

    Ui::QtWidgetsApplication1Class ui;
    int m_nCounter = 1;
};
QtWidgetsApplication.cpp 复制代码
#include "QtWidgetsApplication1.h"
#include <QPushButton>
#include <QThread>
#include <QDebug>

QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

	connect(ui.btnUpdate, &QPushButton::clicked, this, &QtWidgetsApplication1::onUpdate);
	qDebug() << "Main thread ID: " << QThread::currentThreadId();
}

QtWidgetsApplication1::~QtWidgetsApplication1()
{}

void QtWidgetsApplication1::onUpdate()
{
	 Model::GetInstance().Notify();
}

void QtWidgetsApplication1::Execute()
{
    qDebug() << "Current thread ID: " << QThread::currentThreadId();
	if (thread() != QThread::currentThread())
	{
		QMetaObject::invokeMethod(this,
			"doUpdate",
			Qt::QueuedConnection,
			Q_ARG(int, m_nCounter++));
	}
	else
	{
		doUpdate(m_nCounter++);
	}
}

void QtWidgetsApplication1::doUpdate(int v)
{
	if (thread() != QThread::currentThread())
	{
		qFatal("Execute is NOT in GUI thread!");
	}

	ui.lblValue->setText(QString::number(v));
}
  • 模式设计
Observer.h 复制代码
#pragma once
#include <queue>
#include <mutex>
#include <thread>
#include <memory>
#include <vector>

class Observer
{
public:
	virtual ~Observer() = default;
	virtual void Execute() = 0;
};

using ObserverPtr = std::shared_ptr<Observer>;
using WeakObserverPtr = std::weak_ptr<Observer>;

class Model
{
public:
	virtual ~Model();
	static Model& GetInstance()
	{
		static Model model;
		return model;
	}

	/*-----增*删*改*查-----*/
	int64_t Register(ObserverPtr sp);		// 返回句柄,用于注销
	void Unregister(int64_t handle);		// 删除
	void Notify();							// 异步通知(非阻塞)
	void NotifySync();						// 同步通知

private:
	Model();
	struct Slot
	{
		int64_t handle;
		WeakObserverPtr wptr;
	};
	std::vector<Slot>			m_slots;
	std::mutex					m_mtx;
	int64_t						m_nextHandle	= 1;

	/*-----异步通知线程-----*/
	void Worker();
	std::thread					m_worker;
	std::queue<int64_t>			m_taskQueue;
	std::condition_variable		m_cv;
	bool						m_stop			= false;
};
Observer.cpp 复制代码
#include "Observer.h"
#include <iostream>

int64_t Model::Register(ObserverPtr sp)
{
	std::lock_guard<std::mutex> lock(m_mtx);
	m_slots.push_back({ m_nextHandle, sp });
	return m_nextHandle++;
}

void Model::Unregister(int64_t handle)
{
	std::lock_guard<std::mutex> lock(m_mtx);
	m_slots.erase(std::remove_if(m_slots.begin(), m_slots.end(),
		[=](const Slot& s) {return s.handle == handle; }),m_slots.end());
}

// 同步通知(立即执行,会阻塞调用者)
void Model::NotifySync()
{
	std::lock_guard<std::mutex> lock(m_mtx);
	for (auto it = m_slots.begin(); it != m_slots.end();)
	{
		if (auto sp = it->wptr.lock())
		{
			sp->Execute();
			++it;
		}
		else
		{
			it = m_slots.erase(it);
		}
	}
}

// 异步通知(把任务抛给后台线程,调用者立即返回)
void Model::Notify()
{
	{
		std::lock_guard<std::mutex> lock(m_mtx);
		m_taskQueue.push(-1); // -1 代表"通知全部"
	}
	m_cv.notify_one();
}

// 后台worker真正遍历并调用
void Model::Worker()
{
	while (true)
	{
		std::unique_lock<std::mutex> lock(m_mtx);
		m_cv.wait(lock, [this] {return !m_taskQueue.empty() || m_stop; });
		if (m_stop)
		{
			break;
		}
		auto todo = std::move(m_taskQueue);
		lock.unlock();

		while (!todo.empty())
		{
			int64_t cmd = todo.front();
			todo.pop();
			lock.lock();
			for (auto& s : m_slots)
			{
				if (auto sp = s.wptr.lock())
				{
					sp->Execute();
				}
			}
			lock.unlock();
		}
	}
}

Model::Model() :
	m_worker(&Model::Worker, this) {}

Model::~Model()
{
	{
		std::lock_guard<std::mutex> lock(m_mtx);
		m_stop = true;
		m_cv.notify_all();
	}
	m_worker.join();
}
相关推荐
今天要早睡_9 分钟前
C++ 核心语法速过:命名空间、引用、函数重载与 nullptr 深度解析
android·java·c++
汉克老师43 分钟前
CSP-J 初赛(以满分为目标):第二十七课 《图的遍历——BFS广度优先搜索——像“水波纹”一样,一层一层地搜索》
c++·csp-j·小学生·学c++编程
闻缺陷则喜何志丹1 小时前
【动态规划】P3609 [USACO17JAN] Hoof, Paper, Scissor G
c++·算法·动态规划·洛谷
汉克老师1 小时前
CSP-J 初赛(以满分为目标):第二十六课 《图的遍历——DFS深度优先搜索——从“树的先序遍历”走进真正的图世界》
c++·csp-j·小学生·学c++编程
程序猿编码1 小时前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
影视飓风TIM2 小时前
C++哈希表深度剖析:冲突处理、rehash、迭代器与unordered容器封装
c++·哈希算法·散列表
C++ 老炮儿的技术栈2 小时前
Qt5 使用 QPainter 绘制阿基米德螺线
开发语言·c++·windows·qt·代码化
xxwxx__2 小时前
深入理解 C++ STL:stack、queue 与 deque 从使用到底层实现全解析
开发语言·c++·算法
wuminyu3 小时前
JVM锁膨胀与Futex源码解析
java·linux·c语言·jvm·c++
郝学胜-神的一滴3 小时前
C++11 工程级应用 08:Lambda表达式与Tuple元组
开发语言·jvm·c++·python·程序人生·开源