用观察者模式通知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();
}
相关推荐
王老师青少年编程17 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮17 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
样例过了就是过了19 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
谭欣辰19 小时前
C++ 排列组合完整指南
开发语言·c++·算法
橙子也要努力变强20 小时前
信号捕捉底层机制-机理篇2
linux·服务器·c++
盐焗鹌鹑蛋20 小时前
【C++】stack和queue类
c++
郝学胜-神的一滴21 小时前
罗德里格斯旋转公式(Rodrigues‘ Rotation Formula)完整推导
c++·unity·godot·图形渲染·three.js·unreal
lzh2004091921 小时前
深入理解进程:从PCB内核结构到写时拷贝的底层实战
linux·c++
aseity21 小时前
跨平台项目中QString 与 非Qt 跨平台动态库在字符集上的一个实用的互操作约定.
c++·经验分享
CN-Dust1 天前
【C++】while语句例题专题
数据结构·c++·算法